diff --git a/162_simplify_join_keys@main/coverage-report/index.html b/162_simplify_join_keys@main/coverage-report/index.html index 20a3b5ed3..12aa38626 100644 --- a/162_simplify_join_keys@main/coverage-report/index.html +++ b/162_simplify_join_keys@main/coverage-report/index.html @@ -94,7 +94,7 @@ font-size: 11px; }
1 |
- ## TealDataConnection ====+ ## Callable ==== |
||
2 |
- #' @title A `TealDataConnection` class of objects+ #' |
||
3 |
- #' @description `r lifecycle::badge("stable")`+ #' @title A \code{Callable} class of objects |
||
5 |
- #' Objects of this class store the connection to a data source.+ #' @description Object that stores function name with its arguments. Methods to get call and run it. |
||
6 |
- #' It can be a database or server connection.+ #' @keywords internal |
||
8 |
- #' @examples+ Callable <- R6::R6Class( # nolint |
||
9 |
- #' open_fun <- callable_function(data.frame) # define opening function+ "Callable", |
||
10 |
- #' open_fun$set_args(list(x = 1:5)) # define fixed arguments to opening function+ |
||
11 |
- #'+ ## __Public Methods ==== |
||
12 |
- #' close_fun <- callable_function(sum) # define closing function+ public = list( |
||
13 |
- #' close_fun$set_args(list(x = 1:5)) # define fixed arguments to closing function+ #' @description |
||
14 |
- #'+ #' Create a new \code{CallableCode} object |
||
15 |
- #' ping_fun <- callable_function(function() TRUE)+ #' |
||
16 |
- #'+ #' @param env (\code{environment})\cr |
||
17 |
- #' x <- data_connection( # define connection+ #' environment where the call will be evaluated |
||
18 |
- #' ping_fun = ping_fun, # define ping function+ #' |
||
19 |
- #' open_fun = open_fun, # define opening function+ #' @return new \code{CallableCode} object |
||
20 |
- #' close_fun = close_fun # define closing function+ initialize = function(env) { |
||
21 | -+ | 230x |
- #' )+ stopifnot(is.environment(env)) |
22 | -+ | 230x |
- #'+ private$env <- env |
23 | -+ | 230x |
- #' x$set_open_args(args = list(y = letters[1:5])) # define additional arguments if necessary+ logger::log_trace("Callable initialized.") |
24 | -+ | 230x |
- #'+ invisible(self) |
25 |
- #' x$open() # call opening function+ }, |
||
26 |
- #' x$get_open_call() # check reproducible R code+ #' @description |
||
27 |
- #'+ #' Assigns \code{x <- value} object to \code{env}. Assigned object can't |
||
28 |
- #' # get data from connection via TealDataConnector$get_dataset()+ #' be modified within local environment as it will be locked by using |
||
29 |
- #' \dontrun{+ #' \code{lockBinding}. This also means that this object can't be reassigned |
||
30 |
- #' x$open(args = list(x = 1:5, y = letters[1:5])) # able to call opening function with arguments+ #' which will throw an error. |
||
31 |
- #' x$close() # call closing function+ #' @param x (\code{character} value)\cr |
||
32 |
- #' }+ #' name of the variable in class environment |
||
33 |
- #'+ #' @param value (\code{data.frame})\cr |
||
34 |
- TealDataConnection <- R6::R6Class( # nolint+ #' object to be assigned to \code{x} |
||
35 |
- ## __Public Methods ====+ #' |
||
36 |
- "TealDataConnection",+ #' @return (\code{self}) invisibly for chaining. |
||
37 |
- public = list(+ assign_to_env = function(x, value) { |
||
38 |
- #' @description+ # assign variable once |
||
39 | -+ | 63x |
- #' Create a new `TealDataConnection` object+ if (!exists(x, envir = private$env)) { |
40 | -+ | 54x |
- #'+ assign(x, value, envir = private$env) |
41 |
- #' @param open_fun (`CallableFunction`) function to open connection+ |
||
42 |
- #' @param close_fun (`CallableFunction`) function to close connection+ # variable can't be modified |
||
43 | -+ | 54x |
- #' @param ping_fun (`CallableFunction`) function to ping connection+ lockBinding(sym = x, env = private$env) |
44 | -+ | 54x |
- #' @param if_conn_obj optional, (`logical`) whether to store `conn` object returned from opening+ logger::log_trace("Callable$assign_to_env assigned '{ x }' to the environment.") |
45 |
- #' connection+ } |
||
46 |
- #' @return new `TealDataConnection` object+ |
||
47 | -+ | 63x |
- initialize = function(open_fun = NULL, close_fun = NULL, ping_fun = NULL, if_conn_obj = FALSE) {+ return(invisible(self)) |
48 | -29x | +
- checkmate::assert_flag(if_conn_obj)+ }, |
|
49 | -29x | +
- if (!is.null(open_fun)) {+ #' @description |
|
50 | -21x | +
- stopifnot(inherits(open_fun, "Callable"))+ #' Execute \code{Callable} function or code. |
|
51 | -21x | +
- private$set_open_fun(open_fun)+ #' |
|
52 |
- }+ #' @param return (\code{logical} value)\cr |
||
53 | -29x | +
- if (!is.null(close_fun)) {+ #' whether to return an object |
|
54 | -3x | +
- stopifnot(inherits(close_fun, "Callable"))+ #' @param args (\code{NULL} or named \code{list})\cr |
|
55 | -3x | +
- private$set_close_fun(close_fun)+ #' supplied for callable functions only, these are dynamic arguments passed to function. |
|
56 |
- }+ #' Dynamic arguments are executed in this call and are not saved which means that |
||
57 | -29x | +
- if (!is.null(ping_fun)) {+ #' \code{self$get_call()} won't include them later. |
|
58 | -! | +
- stopifnot(inherits(ping_fun, "Callable"))+ #' @param try (\code{logical} value)\cr |
|
59 | -! | +
- private$set_ping_fun(ping_fun)+ #' whether perform function evaluation inside \code{try} clause |
|
60 |
- }+ #' |
||
61 | -29x | +
- private$if_conn_obj <- if_conn_obj+ #' @return nothing or output from function depending on \code{return} |
|
62 |
-
+ #' argument. If \code{run} fails it will return object of class \code{simple-error error} |
||
63 | -29x | +
- private$open_ui <- function(id) {+ #' when \code{try = TRUE} or will stop if \code{try = FALSE}. |
|
64 | -! | +
- NULL+ run = function(return = TRUE, args = NULL, try = FALSE) { |
|
65 | -+ | 150x |
- }+ checkmate::assert_flag(return) |
66 | -29x | +150x |
- private$ping_ui <- function(id) {+ checkmate::assert_list(args, names = "unique", min.len = 0, null.ok = TRUE) |
67 | -! | +150x |
- NULL+ checkmate::assert_flag(try) |
68 |
- }+ |
||
69 | -29x | +
- private$close_ui <- function(id) {+ # args are "dynamic" are used only to evaluate this call |
|
70 | -! | +
- NULL+ # - args not saved to private$call persistently |
|
71 | -+ | 150x |
- }+ expr <- self$get_call(deparse = FALSE, args = args) |
73 | -29x | +150x |
- logger::log_trace(+ res <- tryCatch( |
74 | -29x | +150x |
- sprintf(+ eval(expr, envir = private$env), |
75 | -29x | +150x |
- "TealDataConnection initialized with:%s%s%s%s.",+ error = function(e) e |
76 | -29x | +
- if (!is.null(open_fun)) " open_fun" else "",+ ) |
|
77 | -29x | +150x |
- if (!is.null(close_fun)) " close_fun" else "",+ private$check_run_output(res, try = try) |
78 | -29x | +
- if (!is.null(ping_fun)) " ping_fun" else "",+ |
|
79 | -29x | +145x |
- if (if_conn_obj) " conn" else ""+ logger::log_trace("Callable$run callable has been run.") |
80 | -+ | 145x |
- )+ if (return) { |
81 | -+ | 144x |
- )+ return(res) |
82 | -29x | +
- invisible(self)+ } else { |
|
83 | -+ | 1x |
- },+ return(invisible(NULL)) |
84 |
- #' @description+ } |
||
85 |
- #' Finalize method closing the connection.+ }, |
||
86 |
- #'+ #' @description |
||
87 |
- #' @return NULL+ #' Check if evaluation of the function has not failed. |
||
88 |
- finalize = function() {+ #' |
||
89 | -29x | +
- self$close(silent = TRUE, try = TRUE)+ #' @return (\code{logical}) \code{TRUE} if evaluation of the function failed or \code{FALSE} |
|
90 | -29x | +
- NULL+ #' if evaluation failed or function hasn't yet been called. |
|
91 |
- },+ is_failed = function() { |
||
92 | -+ | 151x |
- #' @description+ return(private$failed) |
93 |
- #' If connection is opened+ }, |
||
94 |
- #'+ #' @description |
||
95 |
- #' If open connection has been successfully evaluated+ #' Get error message from last function execution |
||
97 |
- #' @return (`logical`) if connection is open+ #' @return (\code{character}) object with error message or \code{character(0)} if last |
||
98 |
- is_opened = function() {+ #' function evaluation was successful. |
||
99 | -4x | +
- return(private$opened)+ get_error_message = function() { |
|
100 | -+ | 3x |
- },+ return(private$error_msg) |
101 |
- #' @description+ } |
||
102 |
- #' Check if connection has not failed.+ ), |
||
103 |
- #'+ |
||
104 |
- #' @return (`logical`) `TRUE` if connection failed, else `FALSE`+ ## __Private Fields ==== |
||
105 |
- is_failed = function() {+ private = list( |
||
106 | -! | +
- self$is_open_failed() || self$is_close_failed()+ call = NULL, # a call object |
|
107 |
- },+ env = NULL, # environment where function is called |
||
108 |
- #' @description+ failed = FALSE, |
||
109 |
- #' Run simple application that uses its `ui` and `server` fields to open the+ error_msg = character(0), |
||
110 |
- #' connection.+ ## __Private Methods ==== |
||
111 |
- #'+ |
||
112 |
- #' Useful for debugging+ # The deep clone function deep clones the environment of the Callable so |
||
113 |
- #'+ # that it is distinct for the copy |
||
114 |
- #' @return An object that represents the app+ deep_clone = function(name, value) { |
||
115 | -+ | 155x |
- launch = function() {+ deep_clone_r6(name, value) |
116 | -! | +
- shinyApp(+ }, |
|
117 | -! | +
- ui = fluidPage(+ # Checks output and handles error messages |
|
118 | -! | +
- include_js_files(),+ check_run_output = function(res, try) { |
|
119 | -! | +150x |
- theme = get_teal_bs_theme(),+ if (inherits(res, "error")) { |
120 | -! | +8x |
- fluidRow(+ msg <- conditionMessage(res) |
121 | -! | +8x |
- column(+ is_locked <- grepl(pattern = "cannot change value of locked", x = msg) |
122 | -! | +
- width = 8,+ |
|
123 | -! | +8x |
- offset = 2,+ error_msg <- if (is_locked) { |
124 | -! | +2x |
- tags$div(+ locked_var <- gsub("^.+\\'(.+)\\'$", "\\1", x = msg) |
125 | -! | +2x |
- id = "connection_inputs",+ sprintf( |
126 | -! | +2x |
- self$get_open_ui(id = "data_connection"),+ "Modification of the local variable '%1$s' is not allowed. %2$s '%1$s'", |
127 | -! | +2x |
- actionButton("submit", "Submit"),+ locked_var, |
128 | -! | +2x |
- `data-proxy-click` = "submit" # handled by jscode in custom.js - hit enter to submit+ "Please add proxy variable to CallableCode to obtain results depending on altered" |
129 |
- ),+ ) |
||
130 | -! | +
- shinyjs::hidden(+ } else { |
|
131 | -! | +6x |
- tags$div(+ msg |
132 | -! | +
- id = "connection_set",+ } |
|
133 | -! | +
- div(+ |
|
134 | -! | +8x |
- h3("Connection successfully set."),+ if (try) { |
135 | -! | +3x |
- p("You can close this window and get back to R console.")+ private$failed <- TRUE |
136 | -+ | 3x |
- )+ private$error_msg <- error_msg |
137 | -+ | 3x |
- )+ logger::log_error("Callable$check_run_output { deparse1(error_msg) }.") |
138 |
- )+ } else { |
||
139 | -+ | 5x |
- )+ stop(error_msg, call. = FALSE) |
140 |
- )+ } |
||
141 |
- ),+ } else { |
||
142 | -! | +142x |
- server = function(input, output, session) {+ private$failed <- FALSE |
143 | -! | +142x |
- session$onSessionEnded(stopApp)+ private$error_msg <- character(0) |
144 | -! | +
- preopen_server <- self$get_preopen_server()+ } |
|
145 | -! | +
- if (!is.null(preopen_server)) {+ } |
|
146 | -! | +
- preopen_server(id = "data_connection", connection = self)+ ) |
|
147 |
- }+ ) |
||
148 | -! | +
1 | +
- observeEvent(input$submit, {+ # TealDataConnector ------ |
|||
149 | -! | +|||
2 | +
- rv <- reactiveVal(NULL)+ #' |
|||
150 | -! | +|||
3 | +
- open_server <- self$get_open_server()+ #' |
|||
151 | -! | +|||
4 | +
- if (!is.null(open_server)) {+ #' @title Manage multiple and `TealDatasetConnector` of the same type. |
|||
152 | -! | +|||
5 | +
- rv(open_server(id = "data_connection", connection = self))+ #' |
|||
153 | +6 |
- }+ #' @description `r lifecycle::badge("stable")` |
||
154 | -! | +|||
7 | +
- observeEvent(rv(), {+ #' Class manages `TealDatasetConnector` to specify additional dynamic arguments and to |
|||
155 | -! | +|||
8 | +
- if (self$is_opened()) {+ #' open/close connection. |
|||
156 | -! | +|||
9 | +
- removeUI(sprintf("#%s", session$ns("connection_inputs")))+ #' |
|||
157 | -! | +|||
10 | +
- shinyjs::show("connection_set")+ #' @param connection (`TealDataConnection`)\cr |
|||
158 | -! | +|||
11 | +
- stopApp()+ #' connection to data source |
|||
159 | +12 |
- }+ #' @param connectors (`list` of `TealDatasetConnector` elements)\cr |
||
160 | +13 |
- })+ #' list with dataset connectors |
||
161 | +14 |
- })+ #' |
||
162 | +15 |
- }+ #' @examples |
||
163 | +16 |
- )+ #' library(magrittr) |
||
164 | +17 |
- },+ #' |
||
165 | +18 |
- # ___ open connection -----+ #' random_data_connector <- function(dataname) { |
||
166 | +19 |
- #' @description+ #' fun_dataset_connector( |
||
167 | +20 |
- #' Open the connection.+ #' dataname = dataname, |
||
168 | +21 |
- #'+ #' fun = teal.data::example_cdisc_data, |
||
169 | +22 |
- #' Note that if the connection is already opened then it does nothing.+ #' fun_args = list(dataname = dataname), |
||
170 | +23 |
- #'+ #' ) |
||
171 | +24 |
- #' @param args (`NULL` or named `list`) additional arguments not set up previously+ #' } |
||
172 | +25 |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ #' |
||
173 | +26 |
- #' @param try (`logical`) whether perform function evaluation inside `try` clause+ #' open_fun <- callable_function(library) |
||
174 | +27 |
- #'+ #' open_fun$set_args(list(package = "teal.data")) |
||
175 | +28 |
- #' @return returns `self` if successful or if connection has been already+ #' |
||
176 | +29 |
- #' opened. If `open_fun` fails, app returns an error in form of+ #' con <- data_connection(open_fun = open_fun) |
||
177 | +30 |
- #' `shinyjs::alert` (if `try = TRUE`) or breaks the app (if `try = FALSE`)+ #' con$set_open_server( |
||
178 | +31 |
- #'+ #' function(id, connection) { |
||
179 | +32 |
- open = function(args = NULL, silent = FALSE, try = FALSE) {+ #' moduleServer( |
||
180 | -6x | +|||
33 | +
- logger::log_trace("TealDataConnection$open opening the connection...")+ #' id = id, |
|||
181 | -6x | +|||
34 | +
- checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE)+ #' module = function(input, output, session) { |
|||
182 | -6x | +|||
35 | +
- if (isFALSE(private$check_open_fun(silent = silent))) {+ #' connection$open(try = TRUE) |
|||
183 | -! | +|||
36 | +
- return()+ #' return(invisible(connection)) |
|||
184 | +37 |
- }+ #' } |
||
185 | -6x | +|||
38 | +
- if (isTRUE(private$opened) && isTRUE(private$ping())) {+ #' ) |
|||
186 | -! | +|||
39 | +
- private$opened <- TRUE+ #' } |
|||
187 | -! | +|||
40 | +
- logger::log_trace("TealDataConnection$open connection already opened - skipped.")+ #' ) |
|||
188 | -! | +|||
41 | +
- return(invisible(self))+ #' |
|||
189 | +42 |
- } else {+ #' x <- teal.data:::TealDataConnector$new( |
||
190 | -6x | +|||
43 | +
- open_res <- private$open_fun$run(args = args, try = try)+ #' connection = con, |
|||
191 | -6x | +|||
44 | +
- if (!self$is_open_failed()) {+ #' connectors = list( |
|||
192 | -6x | +|||
45 | +
- private$opened <- TRUE+ #' random_data_connector(dataname = "ADSL"), |
|||
193 | -6x | +|||
46 | +
- if (private$if_conn_obj && !is.null(open_res)) {+ #' random_data_connector(dataname = "ADLB") |
|||
194 | -! | +|||
47 | +
- private$conn <- open_res+ #' ) |
|||
195 | +48 |
-
+ #' ) |
||
196 | -! | +|||
49 | +
- if (!is.null(private$close_fun)) {+ #' |
|||
197 | -! | +|||
50 | +
- private$close_fun$assign_to_env("conn", private$conn)+ #' x$set_ui( |
|||
198 | +51 |
- }+ #' function(id, connection, connectors) { |
||
199 | -! | +|||
52 | +
- if (!is.null(private$ping_fun)) {+ #' ns <- NS(id) |
|||
200 | -! | +|||
53 | +
- private$ping_fun$assign_to_env("conn", private$conn)+ #' tagList( |
|||
201 | +54 |
- }+ #' connection$get_open_ui(ns("open_connection")), |
||
202 | +55 |
- }+ #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1), |
||
203 | -6x | +|||
56 | +
- logger::log_trace("TealDataConnection$open connection opened.")+ #' do.call( |
|||
204 | +57 |
- } else {+ #' what = "tagList", |
||
205 | -! | +|||
58 | +
- private$opened <- FALSE+ #' args = lapply( |
|||
206 | -! | +|||
59 | +
- private$conn <- NULL+ #' connectors, |
|||
207 | -! | +|||
60 | +
- logger::log_error("TealDataConnection$open connection failed to open.")+ #' function(connector) { |
|||
208 | +61 |
- }+ #' div( |
||
209 | +62 |
-
+ #' connector$get_ui( |
||
210 | -6x | +|||
63 | +
- return(invisible(self))+ #' id = ns(connector$get_dataname()) |
|||
211 | +64 |
- }+ #' ), |
||
212 | +65 |
- },+ #' br() |
||
213 | +66 |
-
+ #' ) |
||
214 | +67 |
- #' @description+ #' } |
||
215 | +68 |
- #' Get internal connection object+ #' ) |
||
216 | +69 |
- #'+ #' ) |
||
217 | +70 |
- #' @return `connection` object+ #' ) |
||
218 | +71 |
- get_conn = function() {+ #' } |
||
219 | -3x | +|||
72 | +
- return(private$conn)+ #' ) |
|||
220 | +73 |
- },+ #' |
||
221 | +74 |
- #' @description+ #' x$set_server( |
||
222 | +75 |
- #' Get executed open connection call+ #' function(id, connection, connectors) { |
||
223 | +76 |
- #'+ #' moduleServer( |
||
224 | +77 |
- #' @param deparse (`logical`) whether return deparsed form of a call+ #' id = id, |
||
225 | +78 |
- #' @param args (`NULL` or named `list`) additional arguments not set up previously+ #' module = function(input, output, session) { |
||
226 | +79 |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ #' # opens connection |
||
227 | +80 |
- #'+ #' connection$get_open_server()(id = "open_connection", connection = connection) |
||
228 | +81 |
- #' @return optionally deparsed `call` object+ #' if (connection$is_opened()) { |
||
229 | +82 |
- get_open_call = function(deparse = TRUE, args = NULL, silent = FALSE) {+ #' for (connector in connectors) { |
||
230 | -34x | +|||
83 | +
- checkmate::assert_flag(deparse)+ #' set_args(connector, args = list(n = input$n)) |
|||
231 | -34x | +|||
84 | +
- checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE)+ #' # pull each dataset |
|||
232 | -34x | +|||
85 | +
- if (isFALSE(private$check_open_fun(silent = silent))) {+ #' connector$get_server()(id = connector$get_dataname()) |
|||
233 | -! | +|||
86 | +
- return()+ #' if (connector$is_failed()) { |
|||
234 | +87 |
- }+ #' break |
||
235 | -34x | +|||
88 | +
- open_call <- private$open_fun$get_call(deparse = FALSE, args = args)+ #' } |
|||
236 | +89 |
-
+ #' } |
||
237 | -34x | +|||
90 | +
- if (private$if_conn_obj) {+ #' } |
|||
238 | -! | +|||
91 | +
- open_call <- call("<-", as.name("conn"), open_call)+ #' } |
|||
239 | +92 |
- }+ #' ) |
||
240 | +93 |
-
+ #' } |
||
241 | -34x | +|||
94 | +
- if (isTRUE(deparse)) {+ #' ) |
|||
242 | -32x | +|||
95 | +
- deparse1(open_call, collapse = "\n")+ #' \dontrun{ |
|||
243 | +96 |
- } else {+ #' x$launch() |
||
244 | -2x | +|||
97 | +
- open_call+ #' x$get_datasets() |
|||
245 | +98 |
- }+ #' } |
||
246 | +99 |
- },+ TealDataConnector <- R6::R6Class( # nolint |
||
247 | +100 |
- #' @description+ classname = "TealDataConnector", |
||
248 | +101 |
- #' Get error message from last connection+ inherit = TealDataAbstract, |
||
249 | +102 |
- #'+ |
||
250 | +103 |
- #' @return (`character`)\cr+ ## __Public Methods ==== |
||
251 | +104 |
- #' text of the error message or `character(0)` if last+ public = list( |
||
252 | +105 |
- #' connection was successful.+ #' @description |
||
253 | +106 |
- get_open_error_message = function() {+ #' Create a new `TealDataConnector` object |
||
254 | -! | +|||
107 | +
- return(private$open_fun$get_error_message())+ initialize = function(connection, connectors) { |
|||
255 | -+ | |||
108 | +18x |
- },+ checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1) |
||
256 | +109 |
- #' @description+ |
||
257 | -+ | |||
110 | +18x |
- #' Get shiny server module prior opening connection.+ connectors_names <- vapply(connectors, get_dataname, character(1)) |
||
258 | -+ | |||
111 | +18x |
- #'+ connectors <- setNames(connectors, connectors_names) |
||
259 | +112 |
- #' @return (`function`) shiny server prior opening connection.+ + |
+ ||
113 | +18x | +
+ private$check_names(connectors_names) |
||
260 | +114 |
- get_preopen_server = function() {+ |
||
261 | -! | +|||
115 | +18x |
- return(private$preopen_server)+ if (!missing(connection)) { |
||
262 | -+ | |||
116 | +18x |
- },+ stopifnot(inherits(connection, "TealDataConnection")) |
||
263 | -+ | |||
117 | +18x |
- #' @description+ private$connection <- connection |
||
264 | +118 |
- #' Get shiny server module to open connection.+ } |
||
265 | +119 |
- #'+ |
||
266 | -+ | |||
120 | +18x |
- #' @return (`function`) shiny server to open connection.+ private$datasets <- connectors |
||
267 | +121 |
- get_open_server = function() {+ |
||
268 | -! | +|||
122 | +18x |
- return(private$open_server)+ private$pull_code <- CodeClass$new() |
||
269 | -+ | |||
123 | +18x |
- },+ private$mutate_code <- CodeClass$new() |
||
270 | +124 |
- #' @description+ |
||
271 | -+ | |||
125 | +18x |
- #' Get Shiny module with inputs to open connection+ self$id <- sample.int(1e11, 1, useHash = TRUE) |
||
272 | +126 |
- #'+ |
||
273 | +127 |
- #' @param id `character` shiny element id+ |
||
274 | -+ | |||
128 | +18x |
- #'+ logger::log_trace( |
||
275 | -+ | |||
129 | +18x |
- #' @return (`function`) shiny UI to set arguments to open connection function.+ "TealDataConnector initialized with data: { paste(self$get_datanames(), collapse = ' ') }." |
||
276 | +130 |
- get_open_ui = function(id) {+ ) |
||
277 | -! | +|||
131 | +18x |
- return(private$open_ui(id))+ return(invisible(self)) |
||
278 | +132 |
}, |
||
279 | +133 |
#' @description |
||
280 | +134 |
- #' Check if open connection has not failed.+ #' Prints this `TealDataConnector`. |
||
281 | +135 |
#' |
||
282 | +136 |
- #' @return (`logical`) `TRUE` if open connection failed, else `FALSE`+ #' @param ... additional arguments to the printing method |
||
283 | +137 |
- is_open_failed = function() {+ #' @return invisibly self |
||
284 | -6x | +|||
138 | +
- if (!is.null(private$open_fun)) {+ print = function(...) { |
|||
285 | -6x | +139 | +1x |
- private$open_fun$is_failed()+ check_ellipsis(...) |
286 | +140 |
- } else {+ |
||
287 | -! | +|||
141 | +1x |
- FALSE+ cat(sprintf( |
||
288 | -+ | |||
142 | +1x |
- }+ "A currently %s %s object containing %d TealDataset/TealDatasetConnector object(s) as element(s).\n", |
||
289 | -+ | |||
143 | +1x |
- },+ ifelse(self$get_connection()$is_opened(), "opened", "not yet opened"), |
||
290 | -+ | |||
144 | +1x |
- #' @description+ class(self)[1], |
||
291 | -+ | |||
145 | +1x |
- #' Set open connection function argument+ length(private$datasets) |
||
292 | +146 |
- #'+ )) |
||
293 | -+ | |||
147 | +1x |
- #' @param args (`NULL` or named `list`) with values where list names are argument names+ cat(sprintf( |
||
294 | -+ | |||
148 | +1x |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ "%d of which is/are loaded/pulled:\n", |
||
295 | -+ | |||
149 | +1x |
- #'+ sum(vapply(private$datasets, function(x) x$is_pulled(), FUN.VALUE = logical(1))) |
||
296 | +150 |
- #' @return (`self`) invisibly for chaining.+ )) |
||
297 | +151 |
- set_open_args = function(args, silent = FALSE) {+ |
||
298 | -2x | +152 | +1x |
- checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE)+ for (i in seq_along(private$datasets)) { |
299 | +153 | 2x |
- if (isFALSE(private$check_open_fun(silent = silent))) {+ cat(sprintf("--> Element %d:\n", i)) |
|
300 | -! | +|||
154 | +2x |
- return()+ print(private$datasets[[i]]) |
||
301 | +155 |
} |
||
302 | -2x | +|||
156 | +
- private$open_fun$set_args(args)+ |
|||
303 | -2x | +157 | +1x |
- logger::log_trace("TealDataConnection$set_open_args open args set.")+ invisible(self) |
304 | +158 |
-
+ }, |
||
305 | -2x | +|||
159 | +
- return(invisible(self))+ |
|||
306 | +160 |
- },+ # ___ getters ==== |
||
307 | +161 |
#' @description |
||
308 | +162 |
- #' Set pre-open connection server function+ #' Get connection to data source |
||
309 | +163 |
#' |
||
310 | +164 |
- #' This function will be called before submit button will be hit.+ #' @return connector's connection |
||
311 | +165 |
- #'+ get_connection = function() {+ |
+ ||
166 | +1x | +
+ return(private$connection) |
||
312 | +167 |
- #' @param preopen_module (`function`)\cr+ }, |
||
313 | +168 |
- #' A shiny module server function+ #' @description |
||
314 | +169 |
- #'+ #' Get internal `CodeClass` object |
||
315 | +170 |
- #' @return (`self`) invisibly for chaining.+ #' |
||
316 | +171 |
- set_preopen_server = function(preopen_module) {+ #' @return `CodeClass` |
||
317 | -2x | +|||
172 | +
- stopifnot(inherits(preopen_module, "function"))+ get_code_class = function() { |
|||
318 | -2x | +173 | +30x |
- module_name <- "open_conn"+ all_code <- CodeClass$new()+ |
+
174 | ++ | + | ||
319 | -2x | +175 | +30x |
- if (all(names(formals(preopen_module)) %in% c("input", "output", "session", "connection"))) {+ open_connection_code <- if (!is.null(private$connection)) { |
320 | -1x | +176 | +30x |
- private$preopen_server <- function(input, output, session, connection) {+ private$connection$get_open_call(deparse = TRUE)+ |
+
177 | ++ |
+ } else { |
||
321 | +178 | ! |
- callModule(preopen_module, id = module_name, connection = connection)+ NULL |
|
322 | +179 |
- }+ } |
||
323 | -1x | +|||
180 | +
- } else if (all(names(formals(preopen_module)) %in% c("id", "connection"))) {+ |
|||
324 | -1x | +181 | +30x |
- private$preopen_server <- function(id, connection) {+ if (!is.null(open_connection_code)) { |
325 | -! | +|||
182 | +30x |
- moduleServer(+ all_code$set_code(open_connection_code, dataname = "*open") |
||
326 | -! | +|||
183 | +
- id = id,+ } |
|||
327 | -! | +|||
184 | +30x |
- module = function(input, output, session) {+ datasets_code_class <- private$get_datasets_code_class() |
||
328 | -! | +|||
185 | +30x |
- preopen_module(id = module_name, connection = connection)+ all_code$append(datasets_code_class) |
||
329 | +186 |
- }+ |
||
330 | -+ | |||
187 | +30x |
- )+ close_connection_code <- if (!is.null(private$connection)) { |
||
331 | -+ | |||
188 | +30x |
- }+ private$connection$get_close_call(deparse = TRUE, silent = TRUE) |
||
332 | +189 |
} else { |
||
333 | -! | -
- stop(paste(- |
- ||
334 | -! | -
- "set_preopen_server accepts only a valid shiny module",- |
- ||
335 | +190 | ! |
- "definition with a single additional parameter 'connection'."+ NULL |
|
336 | +191 |
- ))+ } |
||
337 | +192 |
- }+ |
||
338 | -2x | +193 | +30x |
- logger::log_trace("TealDataConnection$set_preopen_server preopen_server set.")+ if (!is.null(close_connection_code)) { |
339 | -+ | |||
194 | +! |
-
+ all_code$set_code(close_connection_code, dataname = "*close") |
||
340 | -2x | +|||
195 | +
- invisible(self)+ } |
|||
341 | +196 |
- },+ |
||
342 | -+ | |||
197 | +30x |
- #' @description+ mutate_code_class <- private$get_mutate_code_class() |
||
343 | -+ | |||
198 | +30x |
- #' Set open connection server function+ all_code$append(mutate_code_class) |
||
344 | +199 |
- #'+ |
||
345 | -+ | |||
200 | +30x |
- #' This function will be called after submit button will be hit. There is no possibility to+ return(all_code) |
||
346 | +201 |
- #' specify some dynamic `ui` as `server` function is executed after hitting submit+ }, |
||
347 | +202 |
- #' button.+ #' @description get the server function |
||
348 | +203 |
#' |
||
349 | +204 |
- #' @param open_module (`function`)\cr+ #' @return the `server` function |
||
350 | +205 |
- #' A shiny module server function that should load data from all connectors+ get_server = function() { |
||
351 | -+ | |||
206 | +2x |
- #'+ if (is.null(private$server)) { |
||
352 | -+ | |||
207 | +! |
- #' @return (`self`) invisibly for chaining.+ stop("No server function set yet. Please use set_server method first.") |
||
353 | +208 |
- set_open_server = function(open_module) {+ } |
||
354 | +209 | 2x |
- stopifnot(inherits(open_module, "function"))+ function(id, connection = private$connection, connectors = private$datasets) { |
|
355 | -2x | +|||
210 | +! |
- module_name <- "open_conn"+ rv <- reactiveVal(NULL) |
||
356 | -2x | +|||
211 | +! |
- if (all(names(formals(open_module)) %in% c("input", "output", "session", "connection"))) {+ moduleServer( |
||
357 | -1x | +|||
212 | +! |
- private$open_server <- function(input, output, session, connection) {+ id = id, |
||
358 | +213 | ! |
- withProgress(message = "Opening connection", value = 1, {+ module = function(input, output, session) { |
|
359 | +214 | ! |
- callModule(open_module, id = module_name, connection = connection)+ private$server(id = "data_input", connection = connection, connectors = connectors) |
|
360 | +215 |
- })+ } |
||
361 | +216 |
- }+ ) |
||
362 | -1x | +|||
217 | +
- } else if (all(names(formals(open_module)) %in% c("id", "connection"))) {+ |
|||
363 | -1x | +|||
218 | +! |
- private$open_server <- function(id, connection) {+ if (self$is_pulled()) { |
||
364 | +219 | ! |
- moduleServer(+ return(rv(TRUE)) |
|
365 | -! | +|||
220 | +
- id = id,+ } else { |
|||
366 | +221 | ! |
- module = function(input, output, session) {+ return(rv(FALSE)) |
|
367 | -! | +|||
222 | +
- withProgress(message = "Opening connection", value = 1, {+ } |
|||
368 | -! | +|||
223 | +
- open_module(id = module_name, connection = connection)+ } |
|||
369 | +224 |
- })+ }, |
||
370 | +225 |
- }+ #' @description get the `preopen` server function |
||
371 | +226 |
- )+ #' |
||
372 | +227 |
- }+ #' @return the `server` function |
||
373 | +228 |
- } else {+ get_preopen_server = function() { |
||
374 | +229 | ! |
- stop(paste(+ function(id, connection = private$connection) { |
|
375 | +230 | ! |
- "set_open_server accepts only a valid shiny module",+ if (!is.null(private$preopen_server)) { |
|
376 | +231 | ! |
- "definition with a single additional parameter 'connection'."+ moduleServer( |
|
377 | -+ | |||
232 | +! |
- ))+ id = id, |
||
378 | -+ | |||
233 | +! |
- }+ module = function(input, output, session) { |
||
379 | -2x | +|||
234 | +! |
- logger::log_trace("TealDataConnection$set_open_server open_server set.")+ private$preopen_server(id = "data_input", connection = connection) |
||
380 | +235 |
-
+ } |
||
381 | -2x | +|||
236 | +
- invisible(self)+ ) |
|||
382 | +237 |
- },+ } |
||
383 | +238 |
- #' @description+ } |
||
384 | +239 |
- #' Set open connection UI function+ }, |
||
385 | +240 |
- #'+ #' @description |
||
386 | +241 |
- #' @param open_module (`function`)\cr+ #' Get Shiny module with inputs for all `TealDatasetConnector` objects |
||
387 | +242 |
- #' shiny module as function. Inputs specified in this `ui` are passed to server module+ #' |
||
388 | +243 |
- #' defined by `set_open_server` method.+ #' @param id `character` shiny element id |
||
389 | +244 |
#' |
||
390 | +245 |
- #' @return (`self`) invisibly for chaining.+ #' @return the `ui` function |
||
391 | +246 |
- set_open_ui = function(open_module) {+ get_ui = function(id) { |
||
392 | -! | +|||
247 | +3x |
- stopifnot(inherits(open_module, "function"))+ if (is.null(private$ui)) { |
||
393 | -! | +|||
248 | +1x |
- stopifnot(identical(names(formals(open_module)), "id"))+ stop("No UI set yet. Please use set_ui method first.") |
||
394 | +249 |
-
+ } |
||
395 | -! | +|||
250 | +2x |
- private$open_ui <- function(id) {+ x <- function(id, connection = private$connection, connectors = private$datasets) { |
||
396 | -! | +|||
251 | +2x |
ns <- NS(id) |
||
397 | -! | +|||
252 | +2x |
tags$div( |
||
398 | -! | +|||
253 | +2x | +
+ h3("Data Connector for:", lapply(self$get_datanames(), code)),+ |
+ ||
254 | +2x |
tags$div( |
||
399 | -! | +|||
255 | +2x |
- id = ns("open_conn"),+ id = ns("data_input"), |
||
400 | -! | +|||
256 | +2x |
- open_module(id = ns("open_conn"))+ private$ui(id = ns("data_input"), connection = connection, connectors = connectors) |
||
401 | +257 |
) |
||
402 | +258 |
) |
||
403 | +259 |
} |
||
404 | -! | +|||
260 | +2x |
- logger::log_trace("TealDataConnection$set_open_ui open_ui set.")+ x(id) |
||
405 | +261 | - - | -||
406 | -! | -
- invisible(self)+ }, |
||
407 | +262 |
- },+ |
||
408 | +263 |
- # ___ close connection -------+ # ___ setters ==== |
||
409 | +264 |
#' @description |
||
410 | +265 |
- #' Close the connection.+ #' Set argument to the `pull_fun` |
||
411 | +266 |
#' |
||
412 | +267 |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ #' @param args (named `list`)\cr |
||
413 | +268 |
- #' @param try (`logical`) whether perform function evaluation inside `try` clause+ #' arguments values as separate list elements named by argument name. These arguments |
||
414 | +269 |
- #'+ #' are passed to each dataset. |
||
415 | +270 |
- #' @return returns (`self`) if successful. For unsuccessful evaluation it+ #' |
||
416 | +271 |
- #' depends on `try` argument: if `try = TRUE` then returns+ #' @return nothing |
||
417 | +272 |
- #' `error`, for `try = FALSE` otherwise+ set_pull_args = function(args) { |
||
418 | -+ | |||
273 | +1x |
- close = function(silent = FALSE, try = FALSE) {+ lapply(private$datasets, function(x) set_args(x, args)) |
||
419 | -33x | +274 | +1x |
- logger::log_trace("TealDataConnection$close closing the connection...")+ logger::log_trace("TealDataConnector$set_pull_args pull args set.") |
420 | -33x | +275 | +1x |
- if (isFALSE(private$check_close_fun(silent = silent))) {+ return(invisible(NULL)) |
421 | -29x | +|||
276 | +
- return()+ }, |
|||
422 | +277 |
- }+ #' @description |
||
423 | -4x | +|||
278 | +
- close_res <- private$close_fun$run(try = try)+ #' Set connector UI function |
|||
424 | -4x | +|||
279 | +
- if (inherits(close_res, "error")) {+ #' |
|||
425 | -! | +|||
280 | +
- logger::log_error("TealDataConnection$close failed to close the connection.")+ #' @param f (`function`)\cr |
|||
426 | -! | +|||
281 | +
- return(close_res)+ #' shiny module as function. Inputs specified in this `ui` are passed to server module |
|||
427 | +282 |
- } else {+ #' defined by `set_server` method.+ |
+ ||
283 | ++ |
+ #'+ |
+ ||
284 | ++ |
+ #' @return nothing+ |
+ ||
285 | ++ |
+ set_ui = function(f) { |
||
428 | +286 | 4x |
- private$opened <- FALSE+ stopifnot(inherits(f, "function")) |
|
429 | +287 | 4x |
- private$conn <- NULL+ stopifnot("id" %in% names(formals(f))) |
|
430 | +288 | 4x |
- logger::log_trace("TealDataConnection$close connection closed.")+ stopifnot(all(c("connection", "connectors") %in% names(formals(f))) || "..." %in% names(formals(f))) |
|
431 | +289 | 4x |
- return(invisible(NULL))+ private$ui <- f |
|
432 | -+ | |||
290 | +4x |
- }+ logger::log_trace("TealDataConnector$set_ui ui set.") |
||
433 | -+ | |||
291 | +4x |
- },+ return(invisible(NULL)) |
||
434 | +292 |
- #' @description+ }, |
||
435 | +293 |
- #' Get executed close connection call+ #' @description |
||
436 | +294 |
- #'+ #' Set connector server function |
||
437 | +295 |
- #' @param deparse (`logical`) whether return deparsed form of a call+ #' |
||
438 | +296 |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ #' This function will be called after submit button will be hit. There is no possibility to |
||
439 | +297 |
- #'+ #' specify some dynamic `ui` as `server` function is executed after hitting submit |
||
440 | +298 |
- #' @return optionally deparsed `call` object+ #' button. |
||
441 | +299 |
- get_close_call = function(deparse = TRUE, silent = FALSE) {- |
- ||
442 | -30x | -
- checkmate::assert_flag(deparse)- |
- ||
443 | -30x | -
- if (isFALSE(private$check_close_fun(silent = silent))) {- |
- ||
444 | -30x | -
- return()+ #' |
||
445 | +300 |
- }- |
- ||
446 | -! | -
- private$close_fun$get_call(deparse = deparse)+ #' @param f (`function`)\cr |
||
447 | +301 |
- },+ #' A shiny module server function that should load data from all connectors |
||
448 | +302 |
- #' @description+ #' |
||
449 | +303 |
- #' Get error message from last connection+ #' @return nothing |
||
450 | +304 |
- #'+ set_server = function(f) { |
||
451 | -+ | |||
305 | +2x |
- #' @return (`character`)\cr+ stopifnot(inherits(f, "function")) |
||
452 | -+ | |||
306 | +2x |
- #' text of the error message or `character(0)` if last+ stopifnot(all(c("id", "connection", "connectors") %in% names(formals(f)))) |
||
453 | -+ | |||
307 | +2x |
- #' connection was successful.+ private$server <- f |
||
454 | -+ | |||
308 | +2x |
- get_close_error_message = function() {+ logger::log_trace("TealDataConnector$set_server server set.") |
||
455 | -! | +|||
309 | +2x |
- return(private$close_fun$get_error_message())+ return(invisible(NULL)) |
||
456 | +310 |
}, |
||
457 | +311 |
#' @description |
||
458 | +312 |
- #' Get shiny server module to close connection.+ #' Set connector pre-open server function |
||
459 | +313 |
#' |
||
460 | +314 |
- #' @return the `server function` to close connection.+ #' This function will be called before submit button will be hit. |
||
461 | +315 |
- get_close_server = function() {- |
- ||
462 | -! | -
- return(private$close_server)+ #' |
||
463 | +316 |
- },+ #' @param f (`function`)\cr |
||
464 | +317 |
- #' @description+ #' A shiny module server function |
||
465 | +318 |
- #' Check if close connection has not failed.+ #' |
||
466 | +319 |
- #'+ #' @return nothing |
||
467 | +320 |
- #' @return (`logical`) `TRUE` if close connection failed, else `FALSE`+ set_preopen_server = function(f) { |
||
468 | -+ | |||
321 | +! |
- is_close_failed = function() {+ stopifnot(inherits(f, "function")) |
||
469 | +322 | ! |
- if (!is.null(private$close_fun)) {+ stopifnot(all(c("id", "connection") %in% names(formals(f)))) |
|
470 | +323 | ! |
- private$close_fun$is_failed()+ private$preopen_server <- f |
|
471 | -+ | |||
324 | +! |
- } else {+ logger::log_trace("TealDataConnector$set_preopen_server preopen_server set.") |
||
472 | +325 | ! |
- FALSE+ return(invisible(NULL)) |
|
473 | +326 |
- }+ }, |
||
474 | +327 |
- },+ |
||
475 | +328 |
-
+ # ___ pull ==== |
||
476 | +329 |
#' @description |
||
477 | +330 |
- #' Set close connection function argument+ #' Load data from each `TealDatasetConnector` |
||
478 | +331 |
#' |
||
479 | +332 |
- #' @param args (named `list`) with values where list names are argument names+ #' @param con_args (`NULL` or named `list`)\cr |
||
480 | +333 |
- #' @param silent (`logical`) whether convert all "missing function" errors to messages+ #' additional dynamic arguments for connection function. `args` will be passed to each |
||
481 | +334 |
- #'+ #' `TealDatasetConnector` object to evaluate `CallableFunction` assigned to |
||
482 | +335 |
- #' @return (`self`) invisibly for chaining.+ #' this dataset. If `args` is null than default set of arguments will be used, otherwise |
||
483 | +336 |
- set_close_args = function(args, silent = FALSE) {- |
- ||
484 | -! | -
- checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE)- |
- ||
485 | -! | -
- if (isFalse(private$check_close_fun(silent = silent))) {- |
- ||
486 | -! | -
- return()+ #' call will be executed on these arguments only (arguments set before will be ignored). |
||
487 | +337 |
- }- |
- ||
488 | -! | -
- private$close_fun$set_args(args)+ #' `pull` function doesn't update reproducible call, it's just evaluate function. |
||
489 | -! | +|||
338 | +
- logger::log_trace("TealDataConnection$set_close_args close_args set")+ #' |
|||
490 | +339 |
-
+ #' @param args (`NULL` or named `list`)\cr |
||
491 | -! | +|||
340 | +
- return(invisible(self))+ #' additional dynamic arguments to pull dataset. `args` will be passed to each |
|||
492 | +341 |
- },+ #' `TealDatasetConnector` object to evaluate `CallableFunction` assigned to |
||
493 | +342 |
-
+ #' this dataset. If `args` is null than default set of arguments will be used, otherwise |
||
494 | +343 |
- #' @description+ #' call will be executed on these arguments only (arguments set before will be ignored). |
||
495 | +344 |
- #' Set close connection UI function+ #' `pull` function doesn't update reproducible call, it's just evaluate function. |
||
496 | +345 |
#' |
||
497 | +346 |
- #' @param close_module (`function`)\cr+ #' @param try (`logical` value)\cr |
||
498 | +347 |
- #' shiny module as function. Inputs specified in this `ui` are passed to server module+ #' whether perform function evaluation inside `try` clause |
||
499 | +348 |
- #' defined by `set_close_server` method.+ #' |
||
500 | +349 |
- #'+ #' @return (`self`) invisibly for chaining. In order to get the data please use `get_datasets` method. |
||
501 | +350 |
- #' @return (`self`) invisibly for chaining.+ pull = function(con_args = NULL, args = NULL, try = TRUE) { |
||
502 | -+ | |||
351 | +3x |
- set_close_ui = function(close_module) {+ logger::log_trace("TealDataConnector$pull pulling data...") |
||
503 | -! | +|||
352 | +
- stopifnot(inherits(close_module, "function"))+ # open connection |
|||
504 | -! | +|||
353 | +3x |
- stopifnot(identical(names(formals(close_module)), "id"))+ if (!is.null(private$connection)) { |
||
505 | -+ | |||
354 | +3x |
-
+ private$connection$open(args = con_args, try = try) |
||
506 | -! | +|||
355 | +
- private$close_ui <- function(id) {+ |
|||
507 | -! | +|||
356 | +3x |
- ns <- NS(id)+ conn <- private$connection$get_conn() |
||
508 | -! | +|||
357 | +3x |
- tags$div(+ for (connector in private$datasets) { |
||
509 | -! | +|||
358 | +4x |
- tags$div(+ connector$get_pull_callable()$assign_to_env("conn", conn) |
||
510 | -! | +|||
359 | +
- id = ns("close_conn"),+ } |
|||
511 | -! | +|||
360 | +
- close_module(id = ns("close_conn"))+ } |
|||
512 | +361 |
- )+ |
||
513 | +362 |
- )+ # load datasets |
||
514 | -+ | |||
363 | +3x |
- }+ for (dataset in private$datasets) { |
||
515 | -! | +|||
364 | +4x |
- logger::log_trace("TealDataConnection$close_ui close_ui set.")+ load_dataset(dataset, args = args) |
||
516 | +365 |
-
+ } |
||
517 | -! | +|||
366 | +
- return(invisible(self))+ |
|||
518 | +367 |
- },+ # close connection+ |
+ ||
368 | +3x | +
+ if (!is.null(private$connection)) private$connection$close(silent = TRUE) |
||
519 | +369 | |||
520 | -+ | |||
370 | +3x |
- #' @description+ logger::log_trace("TealDataConnector$pull data pulled.") |
||
521 | +371 |
- #' Set close-connection server function+ |
||
522 | -+ | |||
372 | +3x |
- #'+ return(invisible(self)) |
||
523 | +373 |
- #' This function will be called after submit button will be hit. There is no possibility to+ }, |
||
524 | +374 |
- #' specify some dynamic `ui` as `server` function is executed after hitting submit+ #' @description |
||
525 | +375 |
- #' button.+ #' Run simple application that uses its `ui` and `server` fields to pull data from |
||
526 | +376 |
- #'+ #' connection. |
||
527 | +377 |
- #' @param close_module (`function`)\cr+ #' |
||
528 | +378 |
- #' A shiny module server function that should load data from all connectors+ #' Useful for debugging |
||
529 | +379 |
#' |
||
530 | +380 |
- #' @return (`self`) invisibly for chaining.+ #' @return An object that represents the app |
||
531 | +381 |
- set_close_server = function(close_module) {+ launch = function() { |
||
532 | -2x | +|||
382 | +
- stopifnot(inherits(close_module, "function"))+ # load TealDatasetConnector objects |
|||
533 | -2x | +|||
383 | +! |
- if (all(names(formals(close_module)) %in% c("input", "output", "session", "connection"))) {+ if (self$is_pulled()) { |
||
534 | -1x | +|||
384 | +! |
- function(input, output, session, connection) {+ stop("All the datasets have already been pulled.") |
||
535 | -! | +|||
385 | +
- connection$close(try = TRUE)+ } |
|||
536 | +386 | |||
537 | +387 | ! |
- if (connection$is_close_failed()) {+ shinyApp( |
|
538 | +388 | ! |
- shinyjs::alert(+ ui = fluidPage( |
|
539 | +389 | ! |
- paste(+ include_js_files(), |
|
540 | +390 | ! |
- "Error closing connection\nError message: ",+ theme = get_teal_bs_theme(), |
|
541 | +391 | ! |
- connection$get_close_error_message()- |
- |
542 | -- |
- )- |
- ||
543 | -- |
- )- |
- ||
544 | -- |
- }+ fluidRow( |
||
545 | +392 | ! |
- invisible(connection)+ column( |
|
546 | -+ | |||
393 | +! |
- }+ width = 8, |
||
547 | -1x | +|||
394 | +! |
- } else if (all(names(formals(close_module)) %in% c("id", "connection"))) {+ offset = 2, |
||
548 | -1x | +|||
395 | +! |
- function(id, connection) {+ tags$div( |
||
549 | +396 | ! |
- moduleServer(+ id = "data_inputs", |
|
550 | +397 | ! |
- id,+ self$get_ui(id = "data_connector"), |
|
551 | +398 | ! |
- function(input, output, session) {+ actionButton("submit", "Submit"), |
|
552 | +399 | ! |
- connection$close(try = TRUE)+ `data-proxy-click` = "submit" # handled by jscode in custom.js - hit enter to submit |
|
553 | +400 |
-
+ ), |
||
554 | +401 | ! |
- if (connection$is_close_failed()) {+ shinyjs::hidden( |
|
555 | +402 | ! |
- shinyjs::alert(+ tags$div( |
|
556 | +403 | ! |
- paste(+ id = "data_loaded", |
|
557 | +404 | ! |
- "Error closing connection\nError message: ",+ div( |
|
558 | +405 | ! |
- connection$get_close_error_message()+ h3("Data successfully loaded."),+ |
+ |
406 | +! | +
+ p("You can close this window and get back to R console.") |
||
559 | +407 |
) |
||
560 | +408 |
) |
||
561 | +409 |
- }- |
- ||
562 | -! | -
- invisible(connection)+ ) |
||
563 | +410 |
- }+ ) |
||
564 | +411 |
) |
||
565 | +412 |
- }+ ), |
||
566 | -+ | |||
413 | +! |
- } else {+ server = function(input, output, session) { |
||
567 | +414 | ! |
- stop(paste(+ session$onSessionEnded(stopApp) |
|
568 | +415 | ! |
- "set_close_server accepts only a valid shiny module",+ self$get_preopen_server()( |
|
569 | +416 | ! |
- "definition with a single additional parameter 'connection'."+ id = "data_connector", |
|
570 | -+ | |||
417 | +! |
- ))+ connection = private$connection |
||
571 | +418 |
- }+ ) |
||
572 | -2x | +|||
419 | +! |
- logger::log_trace("TealDataConnection$set_close_server close_server set.")+ observeEvent(input$submit, { |
||
573 | -+ | |||
420 | +! |
-
+ rv <- reactiveVal(NULL) |
||
574 | -2x | +|||
421 | +! |
- invisible(self)+ rv( |
||
575 | -+ | |||
422 | +! |
- }+ self$get_server()( |
||
576 | -+ | |||
423 | +! |
- ),+ id = "data_connector", |
||
577 | -+ | |||
424 | +! |
- ## __Private Fields ====+ connection = private$connection, |
||
578 | -+ | |||
425 | +! |
- private = list(+ connectors = private$datasets |
||
579 | +426 |
- # callableFunctions+ ) |
||
580 | +427 |
- open_fun = NULL,+ ) |
||
581 | +428 |
- close_fun = NULL,+ |
||
582 | -+ | |||
429 | +! |
- ping_fun = NULL,+ observeEvent(rv(), { |
||
583 | -+ | |||
430 | +! |
-
+ if (self$is_pulled()) { |
||
584 | -+ | |||
431 | +! |
- # connection object+ removeUI(sprintf("#%s", session$ns("data_inputs"))) |
||
585 | -+ | |||
432 | +! |
- if_conn_obj = FALSE,+ shinyjs::show("data_loaded") |
||
586 | -+ | |||
433 | +! |
- conn = NULL,+ stopApp() |
||
587 | +434 |
-
+ } |
||
588 | +435 |
- # shiny elements+ }) |
||
589 | +436 |
- open_ui = NULL,+ }) |
||
590 | +437 |
- close_ui = NULL,+ } |
||
591 | +438 |
- ping_ui = NULL,+ ) |
||
592 | +439 |
- preopen_server = NULL,+ }, |
||
593 | +440 |
- open_server = NULL,+ |
||
594 | +441 |
- close_server = NULL,+ # ___ mutate ==== |
||
595 | +442 |
- ping_server = NULL,+ #' @description |
||
596 | +443 |
- opened = FALSE,+ #' Mutate data by code. |
||
597 | +444 |
-
+ #' |
||
598 | +445 |
- ## __Private Methods ====+ #' @param ... parameters inherited from `TealDataAbstract`. |
||
599 | +446 |
- # need to have a custom deep_clone because one of the key fields are reference-type object+ #' |
||
600 | +447 |
- # in particular: open_fun is a R6 object that wouldn't be cloned using default clone(deep = T)+ #' @return Informational message to not use mutate_data() with `TealDataConnectors`. |
||
601 | +448 |
- deep_clone = function(name, value) {+ mutate = function(...) { |
||
602 | +449 | ! |
- deep_clone_r6(name, value)+ stop("TealDataConnectors do not support mutate_data().+ |
+ |
450 | +! | +
+ Please use mutate_data() with teal_data() or cdisc_data()") |
||
603 | +451 |
}, |
||
604 | +452 |
- check_open_fun = function(silent = FALSE) {+ |
||
605 | -42x | +|||
453 | +
- checkmate::assert_flag(silent)+ # ___ status ==== |
|||
606 | +454 |
-
+ #' @description |
||
607 | -42x | +|||
455 | +
- if (is.null(private$open_fun)) {+ #' Check if pull or connection has not failed. |
|||
608 | -! | +|||
456 | +
- msg <- "Open connection function not set"+ #'+ |
+ |||
457 | ++ |
+ #' @return `TRUE` if pull or connection failed, else `FALSE`+ |
+ ||
458 | ++ |
+ is_failed = function() { |
||
609 | +459 | ! |
- if (silent) {+ private$connection$is_failed() || |
|
610 | +460 | ! |
- return(FALSE)+ any(vapply(private$datasets, function(x) x$is_failed(), logical(1))) |
|
611 | +461 |
- } else {+ } |
||
612 | -! | +|||
462 | +
- stop(msg)+ ), |
|||
613 | +463 |
- }+ ## __Private Fields ==== |
||
614 | +464 |
- } else {+ private = list( |
||
615 | -42x | +|||
465 | +
- return(TRUE)+ server = NULL, # shiny server function |
|||
616 | +466 |
- }+ preopen_server = NULL, # shiny server function |
||
617 | +467 |
- },+ ui = NULL, # shiny ui function |
||
618 | +468 |
- check_close_fun = function(silent = FALSE) {+ connection = NULL, # TealDataConnection |
||
619 | -63x | +|||
469 | +
- checkmate::assert_flag(silent)+ |
|||
620 | +470 |
-
+ ## __Private Methods ==== |
||
621 | -63x | +|||
471 | +
- if (is.null(private$close_fun)) {+ # adds open/close connection code at beginning/end of the dataset code |
|||
622 | -59x | +|||
472 | +
- msg <- "Close connection function not set"+ append_connection_code = function() { |
|||
623 | -59x | +|||
473 | +! |
- if (silent) {+ lapply( |
||
624 | -59x | +|||
474 | +! |
- return(FALSE)+ private$datasets, |
||
625 | -+ | |||
475 | +! |
- } else {+ function(connector) { |
||
626 | +476 | ! |
- stop(msg)+ dataset <- get_dataset(connector) |
|
627 | -+ | |||
477 | +! |
- }+ try( |
||
628 | -+ | |||
478 | +! |
- } else {+ dataset$set_code(code = paste( |
||
629 | -4x | +|||
479 | +! |
- return(TRUE)+ c( |
||
630 | -+ | |||
480 | +! |
- }+ if (!is.null(private$connection)) private$connection$get_open_call(deparse = TRUE), |
||
631 | -+ | |||
481 | +! |
- },+ get_code(dataset, deparse = TRUE, FUN.VALUE = character(1)), |
||
632 | -+ | |||
482 | +! |
- # @description+ if (!is.null(private$connection)) private$connection$get_close_call(deparse = TRUE, silent = TRUE) |
||
633 | +483 |
- # Set close connection function+ ),+ |
+ ||
484 | +! | +
+ collapse = "\n" |
||
634 | +485 |
- #+ )) |
||
635 | +486 |
- # @param fun (`Callable`) function to close connection+ ) |
||
636 | +487 |
- #+ } |
||
637 | +488 |
- # @return (`self`) invisibly for chaining.+ ) |
||
638 | +489 |
- set_close_fun = function(fun) {+ } |
||
639 | -3x | +|||
490 | +
- stopifnot(inherits(fun, "Callable"))+ ) |
|||
640 | -3x | +|||
491 | +
- private$close_fun <- fun+ ) |
|||
641 | -3x | +|||
492 | +
- return(invisible(self))+ |
|||
642 | +493 |
- },+ #' The constructor for `TealDataConnector` class. |
||
643 | +494 |
- # @description+ #' |
||
644 | +495 |
- # Set open connection function+ #' @description `r lifecycle::badge("stable")` |
||
645 | +496 |
- #+ #' @param connection (`TealDataConnection`)\cr |
||
646 | +497 |
- # @param fun (`Callable`) function to open connection+ #' connection to data source |
||
647 | +498 |
- #+ #' @param connectors (`list` of `TealDatasetConnector` elements)\cr |
||
648 | +499 |
- # @return (`self`) invisibly for chaining.+ #' list with dataset connectors |
||
649 | +500 |
- set_open_fun = function(fun) {+ #' |
||
650 | -21x | +|||
501 | +
- stopifnot(inherits(fun, "Callable"))+ #' @examples |
|||
651 | -21x | +|||
502 | +
- private$open_fun <- fun+ #' library(magrittr) |
|||
652 | -21x | +|||
503 | +
- return(invisible(self))+ #' random_data_connector <- function(dataname) { |
|||
653 | +504 |
- },+ #' fun_dataset_connector( |
||
654 | +505 |
- # @description+ #' dataname = dataname, |
||
655 | +506 |
- # Set a ping function+ #' fun = teal.data::example_cdisc_data, |
||
656 | +507 |
- #+ #' fun_args = list(dataname = dataname), |
||
657 | +508 |
- # @param fun (`Callable`) function to ping connection+ #' ) |
||
658 | +509 |
- #+ #' } |
||
659 | +510 |
- # @return (`self`) invisibly for chaining.+ #' |
||
660 | +511 |
- set_ping_fun = function(fun) {+ #' open_fun <- callable_function(library) |
||
661 | -! | +|||
512 | +
- stopifnot(inherits(fun, "Callable"))+ #' open_fun$set_args(list(package = "teal.data")) |
|||
662 | -! | +|||
513 | +
- private$ping_fun <- fun+ #' |
|||
663 | -! | +|||
514 | +
- return(invisible(self))+ #' con <- data_connection(open_fun = open_fun) |
|||
664 | +515 |
- },+ #' con$set_open_server( |
||
665 | +516 |
- # @description+ #' function(id, connection) { |
||
666 | +517 |
- # Ping the connection.+ #' moduleServer( |
||
667 | +518 |
- #+ #' id = id, |
||
668 | +519 |
- # @return (`logical`)+ #' module = function(input, output, session) { |
||
669 | +520 |
- ping = function() {+ #' connection$open(try = TRUE) |
||
670 | -1x | +|||
521 | +
- logger::log_trace("TealDataConnection$ping pinging the connection...")+ #' return(invisible(connection)) |
|||
671 | -1x | +|||
522 | +
- if (!is.null(private$ping_fun)) {+ #' } |
|||
672 | -! | +|||
523 | +
- ping_res <- isTRUE(private$ping_fun$run())+ #' ) |
|||
673 | -! | +|||
524 | +
- logger::log_trace("TealDataConnection$ping ping result: { ping_res }.")+ #' } |
|||
674 | -! | +|||
525 | +
- return(ping_res)+ #' ) |
|||
675 | +526 |
- } else {+ #' |
||
676 | -1x | +|||
527 | +
- return(invisible(NULL))+ #' x <- relational_data_connector( |
|||
677 | +528 |
- }+ #' connection = con, |
||
678 | +529 |
- }+ #' connectors = list( |
||
679 | +530 |
- )+ #' random_data_connector(dataname = "ADSL"), |
||
680 | +531 |
- )+ #' random_data_connector(dataname = "ADLB") |
||
681 | +532 |
-
+ #' ) |
||
682 | +533 |
- #' The constructor for `TealDataConnection` class.+ #' ) |
||
683 | +534 |
#' |
||
684 | +535 |
- #' @description `r lifecycle::badge("stable")`+ #' x$set_ui( |
||
685 | +536 |
- #'+ #' function(id, connection, connectors) { |
||
686 | +537 |
- #' @param open_fun (`CallableFunction`) function to open connection+ #' ns <- NS(id) |
||
687 | +538 |
- #' @param close_fun (`CallableFunction`) function to close connection+ #' tagList( |
||
688 | +539 |
- #' @param ping_fun (`CallableFunction`) function to ping connection+ #' connection$get_open_ui(ns("open_connection")), |
||
689 | +540 |
- #' @param if_conn_obj optional, (`logical`) whether to store `conn` object returned from opening+ #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1), |
||
690 | +541 |
- #'+ #' do.call( |
||
691 | +542 |
- #' @examples+ #' what = "tagList", |
||
692 | +543 |
- #' open_fun <- callable_function(data.frame) # define opening function+ #' args = lapply( |
||
693 | +544 |
- #' open_fun$set_args(list(x = 1:5)) # define fixed arguments to opening function+ #' connectors, |
||
694 | +545 |
- #'+ #' function(connector) { |
||
695 | +546 |
- #' close_fun <- callable_function(sum) # define closing function+ #' div( |
||
696 | +547 |
- #' close_fun$set_args(list(x = 1:5)) # define fixed arguments to closing function+ #' connector$get_ui( |
||
697 | +548 |
- #'+ #' id = ns(connector$get_dataname()) |
||
698 | +549 |
- #' ping_fun <- callable_function(function() TRUE)+ #' ), |
||
699 | +550 |
- #'+ #' br() |
||
700 | +551 |
- #' x <- data_connection( # define connection+ #' ) |
||
701 | +552 |
- #' ping_fun = ping_fun, # define ping function+ #' } |
||
702 | +553 |
- #' open_fun = open_fun, # define opening function+ #' ) |
||
703 | +554 |
- #' close_fun = close_fun # define closing function+ #' ) |
||
704 | +555 | ++ |
+ #' )+ |
+ |
556 | ++ |
+ #' }+ |
+ ||
557 |
#' ) |
|||
705 | +558 |
#' |
||
706 | +559 |
- #' x$set_open_args(args = list(y = letters[1:5])) # define additional arguments if necessary+ #' x$set_server( |
||
707 | +560 |
- #'+ #' function(id, connection, connectors) { |
||
708 | +561 |
- #' x$open() # call opening function+ #' moduleServer( |
||
709 | +562 |
- #' x$get_open_call() # check reproducible R code+ #' id = id, |
||
710 | +563 |
- #'+ #' module = function(input, output, session) { |
||
711 | +564 |
- #' # get data from connection via TealDataConnector$get_dataset()+ #' # opens connection |
||
712 | +565 | ++ |
+ #' connection$get_open_server()(id = "open_connection", connection = connection)+ |
+ |
566 | ++ |
+ #' if (connection$is_opened()) {+ |
+ ||
567 | ++ |
+ #' for (connector in connectors) {+ |
+ ||
568 | ++ |
+ #' set_args(connector, args = list(n = input$n))+ |
+ ||
569 | ++ |
+ #' # pull each dataset+ |
+ ||
570 | ++ |
+ #' connector$get_server()(id = connector$get_dataname())+ |
+ ||
571 | ++ |
+ #' if (connector$is_failed()) {+ |
+ ||
572 | ++ |
+ #' break+ |
+ ||
573 | ++ |
+ #' }+ |
+ ||
574 | ++ |
+ #' }+ |
+ ||
575 | ++ |
+ #' }+ |
+ ||
576 | ++ |
+ #' }+ |
+ ||
577 | ++ |
+ #' )+ |
+ ||
578 | ++ |
+ #' }+ |
+ ||
579 | ++ |
+ #' )+ |
+ ||
580 |
#' \dontrun{ |
|||
713 | +581 |
- #' x$open(args = list(x = 1:5, y = letters[1:5])) # able to call opening function with arguments+ #' x$launch() |
||
714 | +582 |
- #' x$close() # call closing function+ #' x$get_datasets() |
||
715 | +583 |
#' } |
||
716 | +584 |
#' |
||
717 | +585 |
- #' @return `TealDataConnection` object+ #' @return `TealDataConnector` object |
||
718 | +586 |
#' @export |
||
719 | +587 |
- data_connection <- function(open_fun = NULL, close_fun = NULL, ping_fun = NULL, if_conn_obj = FALSE) {+ relational_data_connector <- function(connection, connectors) { |
||
720 | -6x | +588 | +2x |
- TealDataConnection$new(+ stopifnot(inherits(connection, "TealDataConnection")) |
721 | -6x | +589 | +2x |
- open_fun = open_fun, close_fun = close_fun, ping_fun = ping_fun, if_conn_obj = if_conn_obj+ checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1) |
722 | -+ | |||
590 | +2x |
- )+ TealDataConnector$new(connection, connectors) |
||
723 | +591 |
}@@ -5178,14 +5289,14 @@ teal.data coverage - 74.87% |
1 |
- #' Data input for teal app+ #' S3 generic for `to_relational_data` function. |
||
3 |
- #' @description `r lifecycle::badge("stable")`+ #' This function takes an object and converts into a `TealData` object, the primary data |
||
4 |
- #' Function is a wrapper around [teal_data()] and guesses `join_keys`+ #' object for use in teal applications. |
||
5 |
- #' for given datasets which names match ADAM datasets names.+ #' |
||
6 |
- #'+ #' @param data `TealDataset`, `TealDatasetConnector`, `data.frame`, `MultiAssayExperiment`, `list` |
||
7 |
- #' @inheritParams teal_data+ #' or `function` returning a named list. |
||
8 |
- #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr+ #' |
||
9 |
- #' (optional) object with datasets column names used for joining.+ #' @details Passing a `TealData` into this function leaves the object unchanged. |
||
10 |
- #' If empty then it would be automatically derived basing on intersection of datasets primary keys.+ #' |
||
11 |
- #' For ADAM datasets it would be automatically derived.+ #' @return `TealData` object |
||
13 |
- #' @return a `TealData` or `teal_data` object+ #' @examples |
||
15 |
- #' @details This function checks if there were keys added to all data sets+ #' to_relational_data(head(iris)) |
||
16 |
- #'+ #' to_relational_data(dataset("IRIS", head(iris))) |
||
17 |
- #' @export+ #' to_relational_data(list(iris = head(iris), mtcars = head(mtcars))) |
||
19 |
- #' @examples+ #' d_connector <- dataset_connector("iris", callable_function(function() head(iris))) |
||
20 |
- #'+ #' d_connector$pull() |
||
21 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ #' to_relational_data(d_connector) |
||
22 |
- #' ADTTE <- teal.data::example_cdisc_data("ADTTE")+ #' |
||
23 |
- #'+ #' @keywords internal |
||
24 |
- #' cdisc_data(+ #' @export |
||
25 |
- #' ADSL = ADSL,+ to_relational_data <- function(data) { |
||
26 | -+ | 18x |
- #' ADTTE = ADTTE,+ UseMethod("to_relational_data") |
27 |
- #' code = quote({+ } |
||
28 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ |
||
29 |
- #' ADTTE <- teal.data::example_cdisc_data("ADTTE")+ #' @keywords internal |
||
30 |
- #' }),+ #' @export |
||
31 |
- #' join_keys = join_keys(+ to_relational_data.data.frame <- function(data) { # nolint |
||
32 | -+ | 2x |
- #' join_key("ADSL", "ADTTE", c("STUDYID" = "STUDYID", "USUBJID" = "USUBJID"))+ dataname <- deparse(substitute(data, parent.frame()), width.cutoff = 500L) |
33 | -+ | 2x |
- #' )+ if (grepl("\\)$", dataname)) { |
34 | -+ | ! |
- #' )+ stop("Single data.frame shouldn't be provided as a result of a function call. Please name |
35 | -+ | ! |
- cdisc_data <- function(...,+ the object first or use a named list.") |
36 |
- join_keys = teal.data::cdisc_join_keys(...),+ } |
||
37 |
- code = "",+ |
||
38 | -+ | 2x |
- check = FALSE) {+ if (dataname %in% names(default_cdisc_keys)) { |
39 | -21x | +! |
- data_objects <- list(...)+ cdisc_data(cdisc_dataset(dataname, data)) |
40 | -21x | +
- deprecated_join_keys_extract(data_objects, join_keys)+ } else { |
|
41 | -20x | +2x |
- teal_data(..., join_keys = join_keys, code = code, check = check)+ teal_data(dataset(dataname, data)) |
42 |
- }+ } |
||
43 |
-
+ } |
||
44 |
- #' Extrapolate parents from `TealData` classes+ |
||
45 |
- #'+ #' @keywords internal |
||
46 |
- #' `r lifecycle::badge("deprecated")`+ #' @export |
||
47 |
- #'+ to_relational_data.TealDataset <- function(data) { |
||
48 | -+ | 4x |
- #' note: This function will be removed once the following classes are defunct:+ dataname <- get_dataname(data) |
49 |
- #' `TealDataConnector`, `TealDataset`, `TealDatasetConnector`+ |
||
50 | -+ | 4x |
- #'+ if (dataname %in% names(default_cdisc_keys)) { |
51 | -+ | 2x |
- #' @keywords internal+ cdisc_data(data) |
52 |
- deprecated_join_keys_extract <- function(data_objects, join_keys) {+ } else { |
||
53 | -+ | 2x |
- if (+ teal_data(data) |
54 | -21x | +
- !checkmate::test_list(+ } |
|
55 | -21x | +
- data_objects,+ } |
|
56 | -21x | +
- types = c("TealDataConnector", "TealDataset", "TealDatasetConnector")+ |
|
57 |
- )+ #' @keywords internal |
||
58 |
- ) {+ #' @export |
||
59 | -! | +
- return(join_keys)+ to_relational_data.TealDatasetConnector <- function(data) { # nolint |
|
60 | -+ | 1x |
- }+ to_relational_data.TealDataset(data) |
61 |
- # TODO: check if redundant with same call in teal_data body+ } |
||
62 | -21x | +
- update_join_keys_to_primary(data_objects, join_keys)+ |
|
63 |
-
+ #' @keywords internal |
||
64 | -21x | +
- new_parents_fun <- function(data_objects) {+ #' @export |
|
65 | -25x | +
- lapply(+ to_relational_data.list <- function(data) { |
|
66 | -25x | +11x |
- data_objects,+ checkmate::assert_list( |
67 | -25x | +11x |
- function(x) {+ data, |
68 | -47x | +11x |
- if (inherits(x, "TealDataConnector")) {+ types = c("dataset", "data.frame", "MultiAssayExperiment", "TealDataset", "TealDatasetConnector") |
69 | -4x | +
- unlist(new_parents_fun(x$get_items()), recursive = FALSE)+ ) |
|
70 |
- } else {+ |
||
71 | -43x | +11x |
- list(+ call <- substitute(data, parent.frame()) |
72 | -43x | +11x |
- tryCatch(+ list_names <- names(data) |
73 | -43x | +11x |
- x$get_parent(),+ parsed_names <- as.character(call)[-1] |
74 | -43x | +
- error = function(cond) rep(character(0), length(x$get_datanames()))+ |
|
75 |
- )+ if ( |
||
76 |
- )+ ( |
||
77 | -+ | 11x |
- }+ length(list_names) == 0 && |
78 | -+ | 11x |
- }+ length(parsed_names) == 0 && |
79 | -+ | 11x |
- )+ any(sapply(data, inherits, c("dataset", "data.frame", "MultiAssayExperiment"))) |
80 |
- }+ ) || |
||
81 | -+ | 11x |
-
+ (any(list_names == "") && length(parsed_names) == 0) || |
82 | -21x | +11x |
- new_parents <- unlist(new_parents_fun(data_objects), recursive = FALSE)+ (any(is.na(list_names))) |
83 |
-
+ ) { |
||
84 | -21x | +3x |
- names(new_parents) <- unlist(lapply(data_objects, function(x) {+ stop("Unnamed lists shouldn't be provided as input for data. Please use a named list.") |
85 | -42x | +
- if (inherits(x, "TealDataConnector")) {+ } |
|
86 | -4x | +
- lapply(x$get_items(), function(y) y$get_dataname())+ |
|
87 | -+ | 8x |
- } else {+ datasets_list <- lapply( |
88 | -38x | +8x |
- x$get_datanames()+ seq_along(data), |
89 | -+ | 8x |
- }+ function(idx) { |
90 | -+ | 15x |
- }))+ if (is.data.frame(data[[idx]]) || inherits(data[[idx]], "MultiAssayExperiment")) { |
91 | -+ | 12x |
-
+ dataname <- if (length(list_names) == 0 || list_names[[idx]] == "") { |
92 | -21x | +3x |
- if (is_dag(new_parents)) {+ parsed_names[[idx]] |
93 | -1x | +
- stop("Cycle detected in a parent and child dataset graph.")+ } else { |
|
94 | -+ | 9x |
- }+ list_names[[idx]] |
95 | -20x | +
- join_keys$set_parents(new_parents)+ } |
|
96 | -20x | +
- join_keys$update_keys_given_parents()+ |
|
97 | -+ | 12x |
-
+ if (dataname %in% names(default_cdisc_keys)) { |
98 | -20x | +! |
- join_keys+ cdisc_dataset(dataname, data[[idx]]) |
99 |
- }+ } else { |
||
100 | -+ | 12x |
-
+ dataset(dataname, data[[idx]]) |
101 |
- #' Load `TealData` object from a file+ } |
||
102 | -+ | 3x |
- #'+ } else if (inherits(data[[idx]], "TealDataset") || inherits(data[[idx]], "TealDatasetConnector")) { |
103 | -+ | 3x |
- #' @description `r lifecycle::badge("deprecated")`+ data[[idx]] |
104 |
- #'+ } else { |
||
105 | -+ | ! |
- #' @inheritParams teal_data_file+ stop("Unknown class to create TealDataset from.") |
106 |
- #'+ } |
||
107 |
- #' @return `TealData` object+ } |
||
108 |
- #'+ ) |
||
109 |
- #' @export+ |
||
110 | -+ | 8x |
- #'+ if (any(sapply(datasets_list, function(x) inherits(x, "CDISCTealDataset")))) { |
111 | -+ | ! |
- #' @examples+ do.call("cdisc_data", args = datasets_list) |
112 |
- #' file_example <- tempfile(fileext = ".R")+ } else { |
||
113 | -+ | 8x |
- #' writeLines(+ do.call("teal_data", args = datasets_list) |
114 |
- #' text = c(+ } |
||
115 |
- #' "# code>+ } |
||
116 |
- #' ADSL <- teal.data::example_cdisc_data('ADSL')+ |
||
117 |
- #' ADTTE <- teal.data::example_cdisc_data('ADTTE')+ #' @keywords internal |
||
118 |
- #'+ #' @export |
||
119 |
- #' cdisc_data(+ to_relational_data.MultiAssayExperiment <- function(data) { # nolint |
||
120 | -+ | 1x |
- #' cdisc_dataset(\"ADSL\", ADSL), cdisc_dataset(\"ADTTE\", ADTTE),+ dataname <- deparse(substitute(data, parent.frame()), width.cutoff = 500L) |
121 | -+ | 1x |
- #' code = \"ADSL <- teal.data::example_cdisc_data('ADSL')+ if (grepl("\\)$", dataname)) { |
122 | -+ | ! |
- #' ADTTE <- teal.data::example_cdisc_data('ADTTE')\",+ stop("Single data.frame shouldn't be provided as a result of a function call. Please name |
123 | -+ | ! |
- #' check = FALSE+ the object first or use a named list.") |
124 |
- #' )+ } |
||
125 | -+ | 1x |
- #' # <code"+ teal_data(dataset(dataname, data)) |
126 | - |
- #' ),- |
- |
127 | -- |
- #' con = file_example- |
- |
128 | -- |
- #' )- |
- |
129 | -- |
- #'- |
- |
130 | -- |
- #' cdisc_data_file(file_example)- |
- |
131 | -- |
- cdisc_data_file <- function(path, code = get_code(path)) {- |
- |
132 | -2x | -
- lifecycle::deprecate_warn(when = "0.1.3", what = "cdisc_data_file()", with = "teal_data_file()")- |
- |
133 | -2x | -
- object <- object_file(path, "TealData")- |
- |
134 | -2x | -
- object$mutate(code)- |
- |
135 | -2x | -
- return(object)- |
- |
136 | -
} |
@@ -6136,6516 +6177,6788 @@
1 |
- ## TealDataAbstract ====+ #' Retrieve raw data |
||
2 |
- #' @title `TealDataAbstract` class+ #' |
||
3 |
- #'+ #' @param x (`TealDataset`, `TealDatasetConnector`, `TealDataAbstract`)\cr |
||
4 |
- #' @description+ #' object |
||
5 |
- #' Abstract class containing code for handling set of datasets.+ #' @param dataname (`character`)\cr |
||
6 |
- #' @keywords internal+ #' Name of dataset to return raw data for. |
||
7 |
- TealDataAbstract <- R6::R6Class( # nolint+ #' |
||
8 |
- classname = "TealDataAbstract",+ #' @description `r lifecycle::badge("stable")` |
||
9 |
- ## __Public Methods ====+ #' |
||
10 |
- public = list(+ #' @return `data.frame` with the raw data inserted into the R6 objects. In case of |
||
11 |
- #' @description+ #' `TealDataAbstract`, `list` of `data.frame` can be returned |
||
12 |
- #' Cannot create a `TealDataAbstract` object+ #' if user doesn't specify `dataname` - (`get_raw_data` from all datasets). |
||
13 |
- #'+ #' |
||
14 |
- #' @return throws error+ #' @export |
||
15 |
- initialize = function() {+ get_raw_data <- function(x, dataname = NULL) { |
||
16 | -1x | +214x |
- stop("Pure virtual method")+ checkmate::assert_string(dataname, null.ok = TRUE) |
17 | -+ | 213x |
- },+ UseMethod("get_raw_data") |
18 |
- #' @description+ } |
||
19 |
- #' Check if the object raw data is reproducible from the `get_code()` code.+ |
||
20 |
- #' @return+ #' @export |
||
21 |
- #' `NULL` if check step has been disabled+ #' @rdname get_raw_data |
||
22 |
- #' `TRUE` if all the datasets generated from evaluating the+ #' @examples |
||
23 |
- #' `get_code()` code are identical to the raw data, else `FALSE`.+ #' |
||
24 |
- check = function() {+ #' # TealDataset --------- |
||
25 |
- # code can be put only to the mutate with empty code in datasets+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
26 | -57x | +
- res <- if (isFALSE(private$.check)) {+ #' |
|
27 | -44x | +
- NULL+ #' x <- dataset(dataname = "ADSL", x = ADSL) |
|
28 |
- } else {+ #' get_raw_data(x) |
||
29 | -13x | +
- if (length(private$pull_code$code) > 0) {+ get_raw_data.TealDataset <- function(x, dataname = NULL) { |
|
30 | -1x | +192x |
- private$check_combined_code()+ if (!is.null(dataname)) { |
31 | -+ | 2x |
- } else {+ warning("'dataname' argument ignored - TealDataset can contain only one dataset.") |
32 | -12x | +
- all(vapply(+ } |
|
33 | -12x | +192x |
- private$datasets,+ x$get_raw_data() |
34 | -12x | +
- function(x) {+ } |
|
35 | -27x | +
- check_res <- x$check()+ |
|
36 |
- # NULL is still ok+ #' @export |
||
37 | -26x | +
- is.null(check_res) || isTRUE(check_res)+ #' @rdname get_raw_data |
|
38 |
- },+ #' @examples |
||
39 | -12x | +
- logical(1)+ #' |
|
40 |
- ))+ #' # TealDatasetConnector --------- |
||
41 |
- }+ #' library(magrittr) |
||
42 |
- }+ #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>% |
||
43 | -56x | +
- private$check_result <- res+ #' set_args(list(dataname = "ADSL")) |
|
44 | -56x | +
- logger::log_trace("TealDataAbstract$check executed the code to reproduce the data - result: { res }.")+ #' dc <- dataset_connector("ADSL", pull_fun_adsl) |
|
45 | -56x | +
- res+ #' load_dataset(dc) |
|
46 |
- },+ #' get_raw_data(dc) |
||
47 |
- #' @description+ get_raw_data.TealDatasetConnector <- function(x, dataname = NULL) { # nolint |
||
48 | -+ | 17x |
- #' Execute `check()` and raise an error if it's not reproducible.+ if (!is.null(dataname)) { |
49 | -+ | 1x |
- #' @return error if code is not reproducible else invisibly nothing+ warning("'dataname' argument ignored - TealDatasetConnector can contain only one dataset.") |
50 |
- check_reproducibility = function() {+ } |
||
51 | -47x | +17x |
- self$check()+ x$get_raw_data() |
52 | -47x | +
- if (isFALSE(self$get_check_result())) {+ } |
|
53 | -2x | +
- stop("Reproducibility check failed.")+ |
|
54 |
- }+ #' @rdname get_raw_data |
||
55 | -45x | +
- logger::log_trace("TealDataAbstract$check_reproducibility reproducibility check passed.")+ #' @export |
|
56 | -45x | +
- return(invisible(NULL))+ #' @examples |
|
57 |
- },+ #' |
||
58 |
- #' @description+ #' # TealData ---------------- |
||
59 |
- #' Execute mutate code. Using `mutate_data(set).TealDataAbstract`+ #' adsl <- cdisc_dataset( |
||
60 |
- #' does not cause instant execution, the `mutate_code` is+ #' dataname = "ADSL", |
||
61 |
- #' delayed and can be evaluated using this method.+ #' x = teal.data::example_cdisc_data("ADSL"), |
||
62 |
- execute_mutate = function() {+ #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
||
63 | -2x | +
- logger::log_trace("TealDataAbstract$execute_mutate evaluating mutate code...")+ #' ) |
|
64 |
- # this will be pulled already! - not needed?+ #' |
||
65 | -2x | +
- if (length(private$mutate_code$code) == 0) {+ #' adtte <- cdisc_dataset( |
|
66 | -1x | +
- res <- unlist(lapply(+ #' dataname = "ADTTE", |
|
67 | -1x | +
- private$datasets,+ #' x = teal.data::example_cdisc_data("ADTTE"), |
|
68 | -1x | +
- function(x) {+ #' code = "library(teal.data)\nADTTE <- teal.data::example_cdisc_data(\"ADTTE\")" |
|
69 | -2x | +
- if (is_pulled(x)) {+ #' ) |
|
70 | -2x | +
- get_datasets(x)+ #' |
|
71 |
- } else {+ #' rd <- teal.data:::TealData$new(adsl, adtte) |
||
72 | -! | +
- NULL+ #' get_raw_data(rd) |
|
73 |
- }+ #' |
||
74 |
- }+ #' # TealDataConnector -------- |
||
75 |
- ))+ #' library(magrittr) |
||
76 |
- # exit early if mutate isn't required+ #' |
||
77 | -1x | +
- logger::log_trace("TealDataAbstract$execute_mutate no code to evaluate.")+ #' slice_cdisc_data <- function(dataname, n) { |
|
78 | -1x | +
- if (!is.null(res)) {+ #' head(example_cdisc_data(dataname), n) |
|
79 | -1x | +
- res <- stats::setNames(res, vapply(res, get_dataname, character(1)))+ #' } |
|
80 |
- }+ #' |
||
81 | -1x | +
- return(res)+ #' random_data_connector <- function(dataname) { |
|
82 |
- }+ #' fun_dataset_connector( |
||
83 |
-
+ #' dataname = dataname, |
||
84 | -1x | +
- if (inherits(private$mutate_code, "PythonCodeClass")) {+ #' fun = slice_cdisc_data, |
|
85 | -! | +
- items <- lapply(self$get_items(), get_raw_data)+ #' fun_args = list(dataname = dataname), |
|
86 | -! | +
- datasets <- stats::setNames(items, vapply(self$get_items(), get_dataname, character(1)))+ #' ) |
|
87 |
-
+ #' } |
||
88 | -! | +
- new_env <- private$mutate_code$eval(vars = c(datasets, private$mutate_vars))+ #' |
|
89 |
- } else {+ #' open_fun <- callable_function(library) |
||
90 |
- # have to evaluate post-processing code (i.e. private$mutate_code) before returning dataset+ #' open_fun$set_args(list(package = "teal.data")) |
||
91 | -1x | +
- new_env <- new.env(parent = parent.env(globalenv()))+ #' |
|
92 | -1x | +
- for (dataset in self$get_items()) {+ #' con <- data_connection(open_fun = open_fun) |
|
93 | -2x | +
- assign(get_dataname(dataset), get_raw_data(dataset), envir = new_env)+ #' con$set_open_server( |
|
94 |
- }+ #' function(id, connection) { |
||
95 |
-
+ #' moduleServer( |
||
96 | -1x | +
- for (var_idx in seq_along(private$mutate_vars)) {+ #' id = id, |
|
97 | -! | +
- mutate_var <- private$mutate_vars[[var_idx]]+ #' module = function(input, output, session) { |
|
98 | -! | +
- assign(+ #' connection$open(try = TRUE) |
|
99 | -! | +
- x = names(private$mutate_vars)[[var_idx]],+ #' return(invisible(connection)) |
|
100 | -! | +
- value = `if`(+ #' } |
|
101 | -! | +
- inherits(mutate_var, "TealDataset") || inherits(mutate_var, "TealDatasetConnector"),+ #' ) |
|
102 | -! | +
- get_raw_data(mutate_var),+ #' } |
|
103 | -! | +
- mutate_var+ #' ) |
|
104 |
- ),+ #' |
||
105 | -! | +
- envir = new_env+ #' rdc <- relational_data_connector( |
|
106 |
- )+ #' connection = con, |
||
107 |
- }+ #' connectors = list(random_data_connector("ADSL"), random_data_connector("ADLB")) |
||
108 |
-
+ #' ) |
||
109 | -1x | +
- private$mutate_code$eval(envir = new_env)+ #' |
|
110 |
- }+ #' rdc$set_ui( |
||
111 |
-
+ #' function(id, connection, connectors) { |
||
112 | -1x | +
- lapply(+ #' ns <- NS(id) |
|
113 | -1x | +
- self$get_datasets(),+ #' tagList( |
|
114 | -1x | +
- function(x) {+ #' connection$get_open_ui(ns("open_connection")), |
|
115 | -2x | +
- x$recreate(+ #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1), |
|
116 | -2x | +
- x = get(get_dataname(x), new_env)+ #' do.call( |
|
117 |
- )+ #' what = "tagList", |
||
118 |
- }+ #' args = lapply( |
||
119 |
- )+ #' connectors, |
||
120 | -1x | +
- logger::log_trace("TealDataAbstract$execute_mutate evaluated mutate code.")+ #' function(connector) { |
|
121 | -1x | +
- return(invisible(NULL))+ #' div( |
|
122 |
- },+ #' connector$get_ui( |
||
123 |
- #' @description+ #' id = ns(connector$get_dataname()) |
||
124 |
- #' Get result of reproducibility check+ #' ), |
||
125 |
- #' @return `NULL` if check has not been called yet, `TRUE` / `FALSE` otherwise+ #' br() |
||
126 |
- get_check_result = function() {+ #' ) |
||
127 | -49x | +
- private$check_result+ #' } |
|
128 |
- },+ #' ) |
||
129 |
- #' @description+ #' ) |
||
130 |
- #' Get code for all datasets.+ #' ) |
||
131 |
- #' @param dataname (`character`) `dataname` or `NULL` for all datasets+ #' } |
||
132 |
- #' @param deparse (`logical`) whether to return the deparsed form of a call+ #' ) |
||
133 |
- #' @return (`character`) vector of code to generate datasets.+ #' |
||
134 |
- get_code = function(dataname = NULL, deparse = TRUE) {+ #' rdc$set_server( |
||
135 | -47x | +
- checkmate::assert_character(dataname, min.len = 1, null.ok = TRUE, any.missing = FALSE)+ #' function(id, connection, connectors) { |
|
136 | -46x | +
- checkmate::assert_flag(deparse)+ #' moduleServer( |
|
137 |
-
+ #' id = id, |
||
138 | -45x | +
- return(self$get_code_class()$get_code(dataname = dataname, deparse = deparse))+ #' module = function(input, output, session) { |
|
139 |
- },+ #' # opens connection |
||
140 |
- #' @description+ #' connection$get_open_server()(id = "open_connection", connection = connection) |
||
141 |
- #' Get internal `CodeClass` object+ #' if (connection$is_opened()) { |
||
142 |
- #' @param only_pull (`logical` value)\cr+ #' for (connector in connectors) { |
||
143 |
- #' if `TRUE` only code to pull datasets will be returned without the mutate code.+ #' set_args(connector, args = list(n = input$n)) |
||
144 |
- #'+ #' # pull each dataset |
||
145 |
- #' @return `CodeClass`+ #' connector$get_server()(id = connector$get_dataname()) |
||
146 |
- get_code_class = function(only_pull = FALSE) {+ #' if (connector$is_failed()) { |
||
147 | -46x | +
- all_code_class <- CodeClass$new()+ #' break |
|
148 |
-
+ #' } |
||
149 | -46x | +
- pull_code_class <- private$get_pull_code_class()+ #' } |
|
150 | -46x | +
- all_code_class$append(pull_code_class)+ #' } |
|
151 |
-
+ #' } |
||
152 | -46x | +
- datasets_code_class <- private$get_datasets_code_class()+ #' ) |
|
153 | -46x | +
- all_code_class$append(datasets_code_class)+ #' } |
|
154 |
-
+ #' ) |
||
155 | -46x | +
- if (isFALSE(only_pull)) {+ #' |
|
156 | -41x | +
- mutate_code_class <- private$get_mutate_code_class()+ #' \dontrun{ |
|
157 | -41x | +
- all_code_class$append(mutate_code_class)+ #' load_datasets(rdc) |
|
158 |
- }+ #' get_raw_data(rdc) |
||
159 |
-
+ #' } |
||
160 | -46x | +
- return(all_code_class)+ #' |
|
161 |
- },+ #' # TealData (with connectors) -------- |
||
162 |
- #' @description+ #' drc <- cdisc_data(rdc) |
||
163 |
- #' Get names of the datasets.+ #' \dontrun{ |
||
164 |
- #'+ #' get_raw_data(drc) |
||
165 |
- #' @return `character` vector with names of all datasets.+ #' } |
||
166 |
- get_datanames = function() {+ get_raw_data.TealDataAbstract <- function(x, dataname = NULL) { # nolint |
||
167 | -209x | +4x |
- datasets_names <- unname(unlist(lapply(private$datasets, get_dataname)))+ if (!is.null(dataname)) { |
168 | -+ | ! |
-
+ datasets_names <- x$get_datanames() |
169 | -209x | +! |
- return(datasets_names)+ if (dataname %in% datasets_names) { |
170 | -+ | ! |
- },+ if (is_pulled(x$get_items(dataname))) { |
171 | -+ | ! |
- #' @description+ get_raw_data( |
172 | -+ | ! |
- #' Get `TealDataset` object.+ get_dataset(x, dataname = dataname) |
173 |
- #'+ ) |
||
174 |
- #' @param dataname (`character` value)\cr+ } else { |
||
175 | -+ | ! |
- #' name of dataset to be returned. If `NULL`, all datasets are returned.+ stop( |
176 | -+ | ! |
- #'+ sprintf("'%s' has not been pulled yet\n - please use `load_dataset()` first.", dataname), |
177 | -+ | ! |
- #' @return `TealDataset`.+ call. = FALSE |
178 |
- get_dataset = function(dataname = NULL) {+ ) |
||
179 | -5x | +
- checkmate::assert_string(dataname, null.ok = TRUE)+ } |
|
180 |
-
+ } else { |
||
181 | -4x | +! |
- if (length(dataname) == 1) {+ stop("The dataname supplied does not exist.") |
182 | -3x | +
- if (!(dataname %in% self$get_datanames())) {+ } |
|
183 | -1x | +
- stop(paste("dataset", dataname, "not found"))+ } else { |
|
184 | -+ | 4x |
- }+ lapply( |
185 | -+ | 4x |
-
+ get_datasets(x), |
186 | -2x | +4x |
- res <- self$get_datasets()[[dataname]]+ get_raw_data |
187 | -2x | +
- return(res)+ ) |
|
188 |
- } else {+ } |
||
189 | -1x | +
- return(self$get_datasets())+ } |
|
190 | +
1 |
- }+ ## MAETealDataset ==== |
|||
191 | +2 |
- },+ #' |
||
192 | +3 |
- #' @description+ #' @title R6 Class representing a `MultiAssayExperiment` object with its attributes |
||
193 | +4 |
- #' Get `list` of `TealDataset` objects.+ #' |
||
194 | +5 |
- #'+ #' @description `r lifecycle::badge("experimental")` |
||
195 | +6 |
- #' @return `list` of `TealDataset`.+ #' Any `MultiAssayExperiment` object can be stored inside this `MAETealDataset`. |
||
196 | +7 |
- get_datasets = function() {- |
- ||
197 | -62x | -
- if (!self$is_pulled()) {- |
- ||
198 | -2x | -
- stop(- |
- ||
199 | -2x | -
- "Not all datasets have been pulled yet.\n",+ #' Some attributes like colnames, dimension or column names for a specific type will |
||
200 | -2x | +|||
8 | +
- "- Please use `load_datasets()` to retrieve complete results."+ #' be automatically derived. |
|||
201 | +9 |
- )+ #' |
||
202 | +10 |
- }+ #' |
||
203 | -60x | +|||
11 | +
- unlist(lapply(self$get_items(), get_dataset))+ #' @param dataname (`character`)\cr |
|||
204 | +12 |
- },+ #' A given name for the dataset it may not contain spaces |
||
205 | +13 |
- #' @description+ #' @param x (`MultiAssayExperiment`)\cr |
||
206 | +14 |
- #' Get all datasets and all dataset connectors+ #' @param keys optional, (`character`)\cr |
||
207 | +15 |
- #'+ #' A vector of primary keys |
||
208 | +16 |
- #' @param dataname (`character` value)\cr+ #' @param code (`character` or `CodeClass`)\cr |
||
209 | +17 |
- #' name of dataset connector to be returned. If `NULL`, all connectors are returned.+ #' A character string defining the code needed to produce the data set in `x`. |
||
210 | +18 |
- #' @return `list` with all datasets and all connectors+ #' `initialize()` and `recreate()` accept code as `CodeClass` |
||
211 | +19 |
- get_items = function(dataname = NULL) {+ #' which is also needed to preserve the code uniqueness and correct order. |
||
212 | -34x | +|||
20 | +
- checkmate::assert_string(dataname, null.ok = TRUE)+ #' @param label (`character`)\cr |
|||
213 | +21 |
-
+ #' Label to describe the dataset |
||
214 | -34x | +|||
22 | +
- if (length(dataname) == 1) {+ #' @param vars (named `list`)) \cr |
|||
215 | -! | +|||
23 | +
- if (!(dataname %in% self$get_datanames())) {+ #' In case when this object code depends on other `TealDataset` object(s) or |
|||
216 | -! | +|||
24 | +
- stop(paste("dataset", dataname, "not found"))+ #' other constant value, this/these object(s) should be included as named |
|||
217 | +25 |
- }+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
218 | -! | +|||
26 | +
- return(private$datasets[[dataname]])+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
|||
219 | +27 |
- } else {+ #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
220 | -34x | +|||
28 | +
- return(private$datasets)+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
|||
221 | +29 |
- }+ #' are included to this object as local `vars` and they cannot be modified |
||
222 | +30 |
- },+ #' within another dataset. |
||
223 | +31 |
- #' @description+ #' @param metadata (named `list` or `NULL`) \cr |
||
224 | +32 |
- #' Has this data been or will this data be subjected to a reproducibility check+ #' Field containing metadata about the dataset. Each element of the list |
||
225 | +33 |
- #' @return `logical`+ #' should be atomic and of length one. |
||
226 | +34 |
- get_check = function() {+ #' |
||
227 | -3x | +|||
35 | +
- private$.check+ #' @seealso [`TealDataset`] |
|||
228 | +36 |
- },+ #' |
||
229 | +37 |
- #' @field id String used to create unique GUI elements+ MAETealDataset <- R6::R6Class( # nolint |
||
230 | +38 |
- id = NULL,+ "MAETealDataset", |
||
231 | +39 |
- #' @description+ inherit = TealDataset, |
||
232 | +40 |
- #' Check if dataset has already been pulled.+ ## __Public Methods ==== |
||
233 | +41 |
- #'+ public = list( |
||
234 | +42 |
- #' @return `TRUE` if dataset has been already pulled, else `FALSE`+ #' @description |
||
235 | +43 |
- is_pulled = function() {+ #' Create a new object of `MAETealDataset` class |
||
236 | -128x | +|||
44 | +
- all(vapply(private$datasets, is_pulled, logical(1)))+ #' |
|||
237 | +45 |
- },+ initialize = function(dataname, |
||
238 | +46 |
- #' @description+ x, |
||
239 | +47 |
- #' Mutate data by code. Code used in this mutation is not linked to particular+ keys = character(0), |
||
240 | +48 |
- #' but refers to all datasets.+ code = character(0), |
||
241 | +49 |
- #' Consequence of this is that when using `get_code(<dataset>)` this+ label = character(0), |
||
242 | +50 |
- #' part of the code will be returned for each specified dataset. This method+ vars = list(), |
||
243 | +51 |
- #' should be used only if particular call involve changing multiple datasets.+ metadata = NULL) { |
||
244 | -+ | |||
52 | +18x |
- #' Otherwise please use `mutate_dataset`.+ if (!requireNamespace("MultiAssayExperiment", quietly = TRUE)) { |
||
245 | -+ | |||
53 | +! |
- #' Execution of `mutate_code` is delayed after datasets are pulled+ stop("Cannot load MultiAssayExperiment - please install the package or restart your session.") |
||
246 | +54 |
- #' (`isTRUE(is_pulled)`).+ } |
||
247 | -+ | |||
55 | +18x |
- #'+ checkmate::assert_string(dataname) |
||
248 | -+ | |||
56 | +18x |
- #' @param code (`character`) Code to mutate the dataset. Must contain the+ stopifnot(inherits(x, "MultiAssayExperiment")) |
||
249 | -+ | |||
57 | +18x |
- #' `dataset$dataname`+ checkmate::assert_character(keys, any.missing = FALSE) |
||
250 | -+ | |||
58 | +18x |
- #' @param vars (named `list`)) \cr+ checkmate::assert( |
||
251 | -+ | |||
59 | +18x |
- #' In case when this object code depends on other `TealDataset` object(s) or+ checkmate::check_character(code, max.len = 1, any.missing = FALSE), |
||
252 | -+ | |||
60 | +18x |
- #' other constant value, this/these object(s) should be included as named+ checkmate::check_class(code, "CodeClass") |
||
253 | +61 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ ) |
||
254 | -+ | |||
62 | +18x |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ checkmate::assert_character(label, max.len = 1, null.ok = TRUE, any.missing = FALSE) |
||
255 | -+ | |||
63 | +18x |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
256 | +64 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ |
||
257 | +65 |
- #' are included to this object as local `vars` and they cannot be modified+ # validate metadata as a list of length one atomic |
||
258 | -+ | |||
66 | +18x |
- #' within another dataset.+ validate_metadata(metadata) |
||
259 | +67 |
- #'+ |
||
260 | -+ | |||
68 | +18x |
- #' @return self invisibly for chaining+ private$.raw_data <- x |
||
261 | -+ | |||
69 | +18x |
- mutate = function(code, vars = list()) {+ private$metadata <- metadata |
||
262 | -8x | +70 | +18x |
- private$set_mutate_vars(vars)+ private$set_dataname(dataname) |
263 | -8x | +71 | +18x |
- private$set_mutate_code(+ self$set_vars(vars) |
264 | -8x | +72 | +18x |
- code = code,+ self$set_dataset_label(label) |
265 | -8x | +73 | +18x |
- deps = names(vars)+ self$set_keys(keys) |
266 | +74 |
- )+ |
||
267 | -8x | +|||
75 | +
- private$check_result <- NULL+ # needed if recreating dataset - we need to preserve code order and uniqueness |
|||
268 | -8x | +76 | +18x |
- logger::log_trace(+ private$code <- CodeClass$new() |
269 | -8x | +77 | +18x |
- sprintf(+ if (is.character(code)) { |
270 | -8x | +78 | +17x |
- "TealDataAbstract$mutate code (%s lines) and vars (%s) set.",+ self$set_code(code) |
271 | -8x | +|||
79 | +
- length(parse(text = code, keep.source = FALSE)),+ } else { |
|||
272 | -8x | +80 | +1x |
- paste(names(vars), collapse = ", ")+ private$code$append(code) |
273 | +81 |
- )+ } |
||
274 | +82 |
- )+ |
||
275 | -8x | +83 | +18x |
- return(invisible(self))+ logger::log_trace("MAETealDataset$initialize initialized dataset: { deparse1(self$get_dataname()) }.") |
276 | +84 |
- },+ |
||
277 | -+ | |||
85 | +18x |
- #' @description+ return(invisible(self)) |
||
278 | +86 |
- #' Mutate dataset by code.+ }, |
||
279 | +87 |
- #' Execution of `mutate_code` is delayed after datasets are pulled+ # ___ check ==== |
||
280 | +88 |
- #' (`isTRUE(is_pulled)`).+ #' @description |
||
281 | +89 |
- #'+ #' Check to determine if the raw data is reproducible from the `get_code()` code. |
||
282 | +90 |
- #' @param dataname (`character`) `Dataname` to be mutated+ #' @return |
||
283 | +91 |
- #' @param code (`character`) Code to mutate the dataset. Must contain the+ #' `TRUE` if the dataset generated from evaluating the |
||
284 | +92 |
- #' `dataset$dataname`+ #' `get_code()` code is identical to the raw data, else `FALSE`. |
||
285 | +93 |
- #' @param vars (named `list`)) \cr+ check = function() { |
||
286 | -+ | |||
94 | +3x |
- #' In case when this object code depends on other `TealDataset` object(s) or+ logger::log_trace( |
||
287 | -+ | |||
95 | +3x |
- #' other constant value, this/these object(s) should be included as named+ "TealDataset$check executing the code to reproduce dataset: { deparse1(self$get_dataname()) }..." |
||
288 | +96 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ ) |
||
289 | -+ | |||
97 | +3x |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ if (!checkmate::test_character(self$get_code(), len = 1, pattern = "\\w+")) { |
||
290 | -+ | |||
98 | +1x |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ stop( |
||
291 | -+ | |||
99 | +1x |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ sprintf( |
||
292 | -+ | |||
100 | +1x |
- #' are included to this object as local `vars` and they cannot be modified+ "Cannot check preprocessing code of '%s' - code is empty.", |
||
293 | -+ | |||
101 | +1x |
- #' within another dataset.+ self$get_dataname() |
||
294 | +102 |
- #'+ ) |
||
295 | +103 |
- #' @return self invisibly for chaining+ ) |
||
296 | +104 |
- mutate_dataset = function(dataname, code, vars = list()) {- |
- ||
297 | -7x | -
- checkmate::assert_character(dataname, min.len = 1, any.missing = FALSE)- |
- ||
298 | -6x | -
- stopifnot(all(dataname %in% self$get_datanames()))+ } |
||
299 | +105 | |||
300 | -5x | +106 | +2x |
- private$set_mutate_vars(vars = vars)+ new_set <- private$execute_code( |
301 | -5x | +107 | +2x |
- private$set_mutate_code(+ code = self$get_code_class(), |
302 | -5x | +108 | +2x |
- code = code,+ vars = private$vars |
303 | -5x | +|||
109 | +
- dataname = dataname,+ ) |
|||
304 | -5x | +110 | +2x |
- deps = names(vars)+ res_check <- tryCatch( |
305 | +111 |
- )+ {+ |
+ ||
112 | +2x | +
+ identical(self$get_raw_data(), new_set) |
||
306 | +113 |
-
+ }, |
||
307 | -5x | +114 | +2x |
- private$check_result <- NULL+ error = function(e) { |
308 | -5x | +|||
115 | +! |
- logger::log_trace(+ FALSE |
||
309 | -5x | +|||
116 | +
- sprintf(+ } |
|||
310 | -5x | +|||
117 | +
- "TealDataAbstract$mutate code (%s lines) and vars (%s) set for dataset: %s.",+ ) |
|||
311 | -5x | +118 | +2x |
- length(parse(text = code, keep.source = FALSE)),+ logger::log_trace("TealDataset$check { deparse1(self$get_dataname()) } reproducibility result: { res_check }.") |
312 | -5x | +|||
119 | +
- paste(names(vars), collapse = ", "),+ |
|||
313 | -5x | +120 | +2x |
- dataname+ return(res_check) |
314 | +121 |
- )+ }, |
||
315 | +122 |
- )+ #' @description |
||
316 | +123 |
-
+ #' Check if keys has been specified correctly for dataset. Set of `keys` |
||
317 | -5x | +|||
124 | +
- return(invisible(self))+ #' should distinguish unique rows or be `character(0)`. |
|||
318 | +125 |
- },+ #' |
||
319 | +126 |
- #' @description+ #' @return `TRUE` if dataset has been already pulled, else `FALSE` |
||
320 | +127 |
- #' Set reproducibility check+ check_keys = function(keys = private$.keys) { |
||
321 | -+ | |||
128 | +8x |
- #'+ if (length(keys) > 0) { |
||
322 | -+ | |||
129 | +3x |
- #' @param check (`logical`) whether to perform reproducibility check.+ if (!all(keys %in% self$get_colnames())) { |
||
323 | -+ | |||
130 | +1x |
- #'+ stop("Primary keys specifed for ", self$get_dataname(), " do not exist in the data.") |
||
324 | +131 |
- #' @return (`self`) invisibly for chaining.+ } |
||
325 | +132 |
- set_check = function(check = FALSE) {+ |
||
326 | -132x | +133 | +2x |
- checkmate::assert_flag(check)+ duplicates <- get_key_duplicates(as.data.frame(SummarizedExperiment::colData(self$get_raw_data())), keys) |
327 | -131x | +134 | +2x |
- private$.check <- check+ if (nrow(duplicates) > 0) { |
328 | -131x | +135 | +1x |
- logger::log_trace("TealDataAbstract$set_check check set to: { check }.")+ stop( |
329 | -131x | +136 | +1x |
- return(invisible(self))+ "Duplicate primary key values found in the dataset '", self$get_dataname(), "' :\n", |
330 | -+ | |||
137 | +1x |
- },+ paste0(utils::capture.output(print(duplicates))[-c(1, 3)], collapse = "\n"), |
||
331 | -+ | |||
138 | +1x |
- #' @description+ call. = FALSE |
||
332 | +139 |
- #' Set pull code+ ) |
||
333 | +140 |
- #'+ } |
||
334 | +141 |
- #' @param code (`character` value)\cr+ } |
||
335 | +142 |
- #' code to reproduce `data` in `TealDataset` objects. Can't be set if any dataset+ }, |
||
336 | +143 |
- #' has `code` set already.+ #' @description |
||
337 | +144 |
- #'+ #' Derive the column names |
||
338 | +145 |
- #' @return (`self`) invisibly for chaining.+ #' @return `character` vector. |
||
339 | +146 |
- set_pull_code = function(code) {+ get_colnames = function() { |
||
340 | -7x | +147 | +8x |
- checkmate::assert_string(code)+ colnames(SummarizedExperiment::colData(private$.raw_data)) |
341 | -6x | +|||
148 | +
- is_code_set <- vapply(+ }, |
|||
342 | -6x | +|||
149 | +
- self$get_items(),+ #' @description |
|||
343 | -6x | +|||
150 | +
- function(item) {+ #' Derive the column labels |
|||
344 | -11x | +|||
151 | +
- get_code(item, deparse = TRUE) != ""+ #' @return `character` vector. |
|||
345 | +152 |
- },+ get_column_labels = function() { |
||
346 | -6x | +|||
153 | +! |
- logical(1)+ vapply( |
||
347 | -+ | |||
154 | +! |
- )+ X = SummarizedExperiment::colData(private$.raw_data), |
||
348 | -+ | |||
155 | +! |
-
+ FUN.VALUE = character(1), |
||
349 | -6x | +|||
156 | +! |
- is_dataset <- vapply(+ FUN = function(x) { |
||
350 | -6x | +|||
157 | +! |
- self$get_items(),+ label <- attr(x, "label") |
||
351 | -6x | +|||
158 | +! |
- function(item) {+ if (length(label) != 1) { |
||
352 | -11x | +|||
159 | +! |
- inherits(item, "TealDataset")+ NA_character_ |
||
353 | +160 |
- },+ } else { |
||
354 | -6x | +|||
161 | +! |
- logical(1)+ label |
||
355 | +162 |
- )+ } |
||
356 | +163 | - - | -||
357 | -6x | -
- if (any(is_code_set & is_dataset)) {+ } |
||
358 | -2x | +|||
164 | +
- stop(+ ) |
|||
359 | -2x | +|||
165 | +
- "'code' argument should be specified only in the 'cdisc_data' or in 'cdisc_dataset' but not in both",+ }, |
|||
360 | -2x | +|||
166 | +
- call. = FALSE+ #' @description |
|||
361 | +167 |
- )+ #' Get the number of columns of the data |
||
362 | +168 |
- }+ #' @return `numeric` vector |
||
363 | +169 |
-
+ get_ncol = function() { |
||
364 | -4x | +|||
170 | +! |
- if (all(!is_dataset)) {+ ncol(SummarizedExperiment::colData(private$.raw_data)) |
||
365 | -1x | +|||
171 | +
- stop(+ }, |
|||
366 | -1x | -
- "Connectors are reproducible by default and setting 'code' argument might break it",- |
- ||
367 | -1x | +|||
172 | +
- call. = FALSE+ #' @description |
|||
368 | +173 |
- )+ #' Get the number of rows of the data |
||
369 | +174 |
- }+ #' @return `numeric` vector |
||
370 | +175 |
-
+ get_nrow = function() { |
||
371 | -3x | +|||
176 | +! |
- private$pull_code <- private$pull_code$set_code(+ nrow(SummarizedExperiment::colData(private$.raw_data)) |
||
372 | -3x | +|||
177 | +
- code = code,+ }, |
|||
373 | -3x | +|||
178 | +
- dataname = self$get_datanames()+ #' @description |
|||
374 | +179 |
- )+ #' Derive the row names |
||
375 | -3x | +|||
180 | +
- logger::log_trace("TealDataAbstract$set_pull_code pull code set.")+ #' @return `character` vector. |
|||
376 | +181 |
-
+ get_rownames = function() { |
||
377 | -3x | +|||
182 | +! |
- return(invisible(self))+ rownames(SummarizedExperiment::colData(private$.raw_data)) |
||
378 | +183 |
}, |
||
379 | -- | - - | -||
380 | +184 |
#' @description |
||
381 | +185 |
- #' Reassign `vars` in `TealDataset` and `TealDatasetConnector` objects+ #' Prints this `MAETealDataset`. |
||
382 | +186 |
- #' to keep the valid reference after deep cloning+ #' @param ... additional arguments to the printing method |
||
383 | +187 |
- #' For example if `TealDatasetConnector` has a dependency on some `TealDataset`, this+ #' |
||
384 | +188 |
- #' `TealDataset` is reassigned inside of `TealDatasetConnector`.+ #' @return invisibly self |
||
385 | +189 |
- reassign_datasets_vars = function() {+ print = function(...) { |
||
386 | -3x | +|||
190 | +! |
- for (dataset in self$get_items()) {+ cat(sprintf("A MAETealDataset object containing data of %d subjects.\n", self$get_nrow())) |
||
387 | -6x | +|||
191 | +! |
- dataset$reassign_datasets_vars(+ print(MultiAssayExperiment::experiments(self$get_raw_data())) |
||
388 | -6x | +|||
192 | +! |
- datasets = self$get_items()+ invisible(self) |
||
389 | +193 |
- )+ } |
||
390 | +194 |
- }+ ), |
||
391 | -3x | +|||
195 | +
- logger::log_trace("TealDataAbstract$reassign_datasets_vars reassigned vars.")+ ## __Private Fields ==== |
|||
392 | -3x | +|||
196 | +
- invisible(NULL)+ private = list( |
|||
393 | +197 |
- }+ .raw_data = NULL, |
||
394 | +198 |
- ),+ get_class_colnames = function(class_type = "character") {+ |
+ ||
199 | +! | +
+ checkmate::assert_string(class_type) |
||
395 | +200 | |||
396 | -+ | |||
201 | +! |
- ## __Private Fields ====+ return_cols <- private$.colnames[which(vapply( |
||
397 | -+ | |||
202 | +! |
- private = list(+ lapply(SummarizedExperiment::colData(private$.raw_data), class), |
||
398 | -+ | |||
203 | +! |
- datasets = NULL,+ function(x, target_class_name) any(x %in% target_class_name),+ |
+ ||
204 | +! | +
+ logical(1),+ |
+ ||
205 | +! | +
+ target_class_name = class_type |
||
399 | +206 |
- .check = FALSE,+ ))] |
||
400 | +207 |
- check_result = NULL, # TRUE / FALSE after calling check()+ + |
+ ||
208 | +! | +
+ return(return_cols) |
||
401 | +209 |
- mutate_code = NULL, # CodeClass after initialization+ }, |
||
402 | +210 |
- mutate_vars = list(), # named list with vars used to mutate object+ |
||
403 | +211 |
- pull_code = NULL, # CodeClass - code to reproduce loading of TealDataset(s) only+ # Evaluate script code to modify data or to reproduce data |
||
404 | +212 |
-
+ # |
||
405 | +213 |
- ## __Private Methods ====+ # @param code (`CodeClass`) the object storing the code to execute |
||
406 | +214 |
- # need to have a custom deep_clone because one of the key fields are reference-type object+ # @param vars (named `list`) additional pre-requisite vars to execute code |
||
407 | +215 |
- # in particular: datasets is a list of R6 objects that wouldn't be cloned using default clone(deep = T)+ # @return (`environment`) which stores modified `x` |
||
408 | +216 |
- deep_clone = function(name, value) {+ execute_code = function(code, vars = list()) { |
||
409 | -222x | +217 | +2x |
- deep_clone_r6(name, value)+ stopifnot(inherits(code, "CodeClass")) |
410 | -+ | |||
218 | +2x |
- },+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
411 | +219 |
- check_combined_code = function() {+ |
||
412 | -4x | +220 | +2x |
execution_environment <- new.env(parent = parent.env(globalenv())) |
413 | -4x | +|||
221 | +
- self$get_code_class(only_pull = TRUE)$eval(envir = execution_environment)+ |
|||
414 | -4x | +|||
222 | +
- res <- all(vapply(+ # set up environment for execution |
|||
415 | -4x | +223 | +2x |
- Filter(is_pulled, self$get_items()),+ for (vars_idx in seq_along(vars)) { |
416 | -4x | +|||
224 | +! |
- function(dataset) {+ var_name <- names(vars)[[vars_idx]] |
||
417 | -8x | +|||
225 | +! |
- data <- get_raw_data(dataset)+ var_value <- vars[[vars_idx]] |
||
418 | -8x | +|||
226 | +! |
- data_from_code <- get(get_dataname(dataset), execution_environment)+ if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) { |
||
419 | -7x | +|||
227 | +! |
- identical(data, data_from_code)+ var_value <- get_raw_data(var_value) |
||
420 | +228 |
- },+ } |
||
421 | -4x | +|||
229 | +! |
- logical(1)+ assign(envir = execution_environment, x = var_name, value = var_value) |
||
422 | +230 |
- ))+ } |
||
423 | -3x | +|||
231 | +
- logger::log_trace("TealDataAbstract$check_combined_code reproducibility result of the combined code: { res }.")+ |
|||
424 | -3x | +|||
232 | +
- res+ # execute |
|||
425 | -+ | |||
233 | +2x |
- },+ code$eval(envir = execution_environment) |
||
426 | +234 |
- get_datasets_code_class = function() {+ |
||
427 | -78x | +235 | +2x |
- res <- CodeClass$new()+ if (!inherits(execution_environment[[self$get_dataname()]], "MultiAssayExperiment")) { |
428 | -78x | +|||
236 | +! |
- if (is.null(private$datasets)) {+ out_msg <- sprintf( |
||
429 | +237 | ! |
- return(res)+ "\n%s\n\n - Code from %s needs to return a MultiAssayExperiment assigned to an object of dataset name.", |
|
430 | -+ | |||
238 | +! |
- }+ self$get_code(), |
||
431 | -78x | +|||
239 | +! |
- for (dataset in private$datasets) {+ self$get_dataname() |
||
432 | -164x | +|||
240 | +
- res$append(dataset$get_code_class())+ ) |
|||
433 | +241 |
- }+ |
||
434 | -78x | +|||
242 | +! |
- return(res)+ rlang::with_options(+ |
+ ||
243 | +! | +
+ .expr = stop(out_msg, call. = FALSE),+ |
+ ||
244 | +! | +
+ warning.length = max(min(8170, nchar(out_msg) + 30), 100) |
||
435 | +245 |
- },+ ) |
||
436 | +246 |
- get_mutate_code_class = function() {+ } |
||
437 | -71x | +|||
247 | +
- res <- CodeClass$new()+ |
|||
438 | -71x | +248 | +2x |
- res$append(list_to_code_class(private$mutate_vars))+ new_set <- execution_environment[[self$get_dataname()]] |
439 | -71x | +|||
249 | +
- res$append(private$mutate_code)+ |
|||
440 | -71x | +250 | +2x |
- return(res)+ return(new_set) |
441 | +251 |
- },+ } |
||
442 | +252 |
- get_pull_code_class = function() {+ ) |
||
443 | -48x | +|||
253 | +
- res <- CodeClass$new()+ ) |
|||
444 | -48x | +|||
254 | +
- res$append(private$pull_code)+ |
|||
445 | -48x | +|||
255 | +
- return(res)+ #' S3 method to construct an `MAETealDataset` object from `MultiAssayExperiment` |
|||
446 | +256 |
- },+ #' |
||
447 | +257 |
- set_mutate_code = function(code, dataname = self$get_datanames(), deps = names(private$mutate_vars)) {+ #' @rdname dataset |
||
448 | -16x | +|||
258 | +
- checkmate::assert(+ #' |
|||
449 | -16x | +|||
259 | +
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ #' @examples |
|||
450 | -16x | +|||
260 | +
- checkmate::check_class(code, "PythonCodeClass")+ #' # Simple example |
|||
451 | +261 |
- )+ #' utils::data(miniACC, package = "MultiAssayExperiment") |
||
452 | +262 |
-
+ #' mae_d <- dataset( |
||
453 | -14x | +|||
263 | +
- if (inherits(code, "PythonCodeClass")) {+ #' "MAE", |
|||
454 | -! | +|||
264 | +
- r <- PythonCodeClass$new()+ #' miniACC, |
|||
455 | -! | +|||
265 | +
- r$append(private$mutate_code)+ #' keys = c("STUDYID", "USUBJID"), |
|||
456 | -! | +|||
266 | +
- private$mutate_code <- r+ #' metadata = list(type = "example") |
|||
457 | +267 |
-
+ #' ) |
||
458 | -! | +|||
268 | +
- code <- code$get_code()+ #' mae_d$get_dataname() |
|||
459 | +269 |
- }+ #' mae_d$get_dataset_label() |
||
460 | +270 |
-
+ #' mae_d$get_metadata() |
||
461 | -14x | +|||
271 | +
- if (length(code) > 0 && code != "") {+ #' mae_d$get_code() |
|||
462 | -14x | +|||
272 | +
- private$mutate_code$set_code(code = code, dataname = dataname, deps = deps)+ #' mae_d$get_raw_data() |
|||
463 | +273 |
- }+ #' @export |
||
464 | +274 |
-
+ dataset.MultiAssayExperiment <- function(dataname, # nolint |
||
465 | -14x | +|||
275 | +
- return(invisible(self))+ x, |
|||
466 | +276 |
- },+ keys = character(0), |
||
467 | +277 |
- set_mutate_vars = function(vars) {+ label = data_label(x), |
||
468 | -17x | +|||
278 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ code = character(0), |
|||
469 | -15x | +|||
279 | +
- if (length(vars) > 0) {+ vars = list(), |
|||
470 | -2x | +|||
280 | +
- private$mutate_vars <- c(+ metadata = NULL) { |
|||
471 | -2x | +281 | +4x |
- private$mutate_vars,+ if (!requireNamespace("MultiAssayExperiment", quietly = TRUE)) { |
472 | -2x | +|||
282 | +! |
- vars[!names(vars) %in% names(private$mutate_vars)]+ stop("Cannot load MultiAssayExperiment - please install the package or restart your session.") |
||
473 | +283 |
- )+ } |
||
474 | -+ | |||
284 | +4x |
- }+ checkmate::assert_string(dataname) |
||
475 | -+ | |||
285 | +4x |
-
+ checkmate::assert( |
||
476 | -15x | +286 | +4x |
- return(invisible(self))+ checkmate::check_character(code, max.len = 1, any.missing = FALSE), |
477 | -+ | |||
287 | +4x |
- },+ checkmate::check_class(code, "CodeClass") |
||
478 | +288 |
- check_names = function(x) {+ ) |
||
479 | -149x | -
- if (any(vapply(x, identical, logical(1), y = ""))) {- |
- ||
480 | -! | +289 | +4x |
- stop("Cannot extract some dataset names")+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
481 | +290 |
- }+ |
||
482 | -149x | +291 | +4x |
- if (any(duplicated(x))) {+ MAETealDataset$new( |
483 | -1x | +292 | +4x |
- stop("TealDatasets names should be unique")+ dataname = dataname, |
484 | -+ | |||
293 | +4x |
- }+ x = x, |
||
485 | -148x | +294 | +4x |
- if (any(x %in% self$get_datanames())) {+ keys = keys, |
486 | -! | +|||
295 | +4x |
- stop("Some datanames already exists")+ code = code, |
||
487 | -+ | |||
296 | +4x |
- }+ label = label, |
||
488 | -148x | +297 | +4x |
- return(TRUE)+ vars = vars, |
489 | -+ | |||
298 | +4x |
- }+ metadata = metadata |
||
490 | +299 |
) |
||
491 | +300 |
- )+ } |
1 | +301 |
- ## TealDatasetConnector ====+ |
|
2 | +302 |
- #'+ #' The constructor of `MAETealDataset` |
|
3 | +303 |
#' |
|
4 | +304 |
- #' @title A `TealDatasetConnector` class of objects+ #' @description `r lifecycle::badge("deprecated")` |
|
5 | +305 |
#' |
|
6 | +306 |
- #' @description `r lifecycle::badge("stable")`+ #' @inheritParams dataset |
|
7 | +307 |
- #' Objects of this class store the connection function to fetch a single dataset.+ #' @param x (`MultiAssayExperiment`) |
|
8 | +308 |
- #' Note that for some specific connection types,+ #' |
|
9 | +309 |
- #' an object of class `TealDataConnection` must be provided.+ #' @examples |
|
10 | +310 |
- #' Data can be pulled via the `pull` method and accessed directly+ #' # Simple example |
|
11 | +311 |
- #' through the `dataset` active binding.+ #' utils::data(miniACC, package = "MultiAssayExperiment") |
|
12 | +312 |
- #' Pulled data inherits from the class [`TealDataset`]+ #' mae_d <- dataset("MAE", miniACC) |
|
13 | +313 |
- #'+ #' mae_d$get_dataname() |
|
14 | +314 |
- #' @param dataname (`character`)\cr+ #' mae_d$get_dataset_label() |
|
15 | +315 |
- #' A given name for the dataset it may not contain spaces+ #' mae_d$get_code() |
|
16 | +316 |
- #'+ #' mae_d$get_raw_data() |
|
17 | +317 |
- #' @param pull_callable (`CallableFunction`)\cr+ #' @export |
|
18 | +318 |
- #' function with necessary arguments set to fetch data from connection.+ mae_dataset <- function(dataname, |
|
19 | +319 |
- #'+ x, |
|
20 | +320 |
- #' @param keys optional, (`character`)\cr+ label = data_label(x), |
|
21 | +321 |
- #' vector of dataset primary keys column names+ code = character(0), |
|
22 | +322 |
- #'+ vars = list()) { |
|
23 | -+ | ||
323 | +! |
- #' @param label (`character`)\cr+ lifecycle::deprecate_soft( |
|
24 | -+ | ||
324 | +! |
- #' Label to describe the dataset.+ when = "0.10.1", |
|
25 | -+ | ||
325 | +! |
- #'+ what = "teal.data::mae_dataset()", |
|
26 | -+ | ||
326 | +! |
- #' @param code (`character`)\cr+ with = "teal.data::dataset()" |
|
27 | +327 |
- #' A character string defining code to modify `raw_data` from this dataset. To modify+ ) |
|
28 | +328 |
- #' current dataset code should contain at least one assignment to object defined in `dataname`+ |
|
29 | -+ | ||
329 | +! |
- #' argument. For example if `dataname = ADSL` example code should contain+ if (!inherits(x, "MultiAssayExperiment")) { |
|
30 | -+ | ||
330 | +! |
- #' `ADSL <- <some R code>`. Can't be used simultaneously with `script`+ stop("Argument x must be a MultiAssayExperiment object") |
|
31 | +331 |
- #'+ } |
|
32 | +332 |
- #' @param script (`character`)\cr+ |
|
33 | -+ | ||
333 | +! |
- #' Alternatively to `code` - location of the file containing modification code.+ dataset( |
|
34 | -+ | ||
334 | +! |
- #' Can't be used simultaneously with `script`.+ dataname = dataname, |
|
35 | -+ | ||
335 | +! |
- #'+ x = x, |
|
36 | -+ | ||
336 | +! |
- #' @param vars (named `list`)) \cr+ code = code, |
|
37 | -+ | ||
337 | +! |
- #' In case when this object code depends on other `TealDataset` object(s) or+ label = label, |
|
38 | -+ | ||
338 | +! |
- #' other constant value, this/these object(s) should be included as named+ vars = vars |
|
39 | +339 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ ) |
|
40 | +340 |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ } |
41 | +1 |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ ## TealDataset ==== |
||
42 | +2 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ #' |
||
43 | +3 |
- #' are included to this object as local `vars` and they cannot be modified+ #' |
||
44 | +4 |
- #' within another dataset.+ #' @title R6 Class representing a dataset with its attributes |
||
45 | +5 |
#' |
||
46 | +6 |
- #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr+ #' @description `r lifecycle::badge("stable")` |
||
47 | +7 |
- #' Field containing either the metadata about the dataset (each element of the list+ #' Any `data.frame` object can be stored inside this object. |
||
48 | +8 |
- #' should be atomic and length one) or a `CallableFuntion` to pull the metadata+ #' Some attributes like colnames, dimension or column names for a specific type will |
||
49 | +9 |
- #' from a connection. This should return a `list` or an object which can be+ #' be automatically derived. |
||
50 | +10 |
- #' converted to a list with `as.list`.+ #' |
||
51 | +11 |
- TealDatasetConnector <- R6::R6Class( # nolint+ #' @param dataname (`character`)\cr |
||
52 | +12 |
-
+ #' A given name for the dataset it may not contain spaces |
||
53 | +13 |
- ## __Public Methods ====+ #' @param x (`data.frame`)\cr |
||
54 | +14 |
- classname = "TealDatasetConnector",+ #' @param keys optional, (`character`)\cr |
||
55 | +15 |
- public = list(+ #' Vector with primary keys |
||
56 | +16 |
- #' @description+ #' @param code (`character`)\cr |
||
57 | +17 |
- #' Create a new `TealDatasetConnector` object. Set the pulling function+ #' A character string defining the code needed to produce the data set in `x`. |
||
58 | +18 |
- #' `CallableFunction` which returns a `data.frame` or `MultiAssayExperiment`,+ #' `initialize()` and `recreate()` accept code as `CodeClass` |
||
59 | +19 |
- #' e.g. by reading from a function or creating it on the fly.+ #' which is also needed to preserve the code uniqueness and correct order. |
||
60 | +20 |
- initialize = function(dataname,+ #' @param label (`character`)\cr |
||
61 | +21 |
- pull_callable,+ #' Label to describe the dataset |
||
62 | +22 |
- keys = character(0),+ #' @param vars (named `list`)) \cr |
||
63 | +23 |
- label = character(0),+ #' In case when this object code depends on other `TealDataset` object(s) or |
||
64 | +24 |
- code = character(0),+ #' other constant value, this/these object(s) should be included as named |
||
65 | +25 |
- vars = list(),+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
66 | +26 |
- metadata = NULL) {- |
- ||
67 | -182x | -
- private$set_pull_callable(pull_callable)- |
- ||
68 | -182x | -
- private$set_var_r6(vars)- |
- ||
69 | -182x | -
- private$set_pull_vars(vars)+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
||
70 | +27 |
-
+ #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
71 | -182x | +|||
28 | +
- private$set_dataname(dataname)+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
|||
72 | -182x | +|||
29 | +
- private$set_metadata(metadata)+ #' are included to this object as local `vars` and they cannot be modified |
|||
73 | +30 |
-
+ #' within another dataset. |
||
74 | -182x | +|||
31 | +
- self$set_dataset_label(label)+ #' @param metadata (named `list` or `NULL`) \cr |
|||
75 | -182x | +|||
32 | +
- self$set_keys(keys)+ #' Field containing metadata about the dataset. Each element of the list |
|||
76 | +33 |
-
+ #' should be atomic and of length one. |
||
77 | -182x | +|||
34 | +
- if (length(code) > 0) {+ #' |
|||
78 | +35 |
- # just needs a dummy TealDataset object to store mutate code, hence col = 1+ #' @seealso [`MAETealDataset`] |
||
79 | -1x | +|||
36 | +
- private$dataset <- TealDataset$new(dataname = self$get_dataname(), x = data.frame(col = 1))+ #' |
|||
80 | -1x | +|||
37 | +
- private$dataset$mutate(code = code, vars = vars, force_delay = TRUE)+ TealDataset <- R6::R6Class( # nolint |
|||
81 | +38 |
- }+ "TealDataset", |
||
82 | +39 | |||
83 | -182x | -
- logger::log_trace("TealDatasetConnector initialized for dataset: { deparse1(self$get_dataname()) }.")- |
- ||
84 | +40 |
-
+ ## __Public Methods ==== |
||
85 | -182x | +|||
41 | +
- return(invisible(self))+ public = list( |
|||
86 | +42 |
- },+ #' @description |
||
87 | +43 |
- #' @description+ #' Create a new object of `TealDataset` class |
||
88 | +44 |
- #' Prints this `TealDatasetConnector`.+ initialize = function(dataname, |
||
89 | +45 |
- #'+ x, |
||
90 | +46 |
- #' @param ... additional arguments to the printing method+ keys = character(0), |
||
91 | +47 |
- #' @return invisibly self+ code = character(0), |
||
92 | +48 |
- print = function(...) {+ label = character(0), |
||
93 | -6x | +|||
49 | +
- check_ellipsis(...)+ vars = list(), |
|||
94 | +50 |
-
+ metadata = NULL) { |
||
95 | -6x | +51 | +490x |
- cat(sprintf(+ checkmate::assert_string(dataname) |
96 | -6x | +52 | +490x |
- "A %s object, named %s, containing a TealDataset object that has %sbeen loaded/pulled%s\n",+ checkmate::assert_data_frame(x) |
97 | -6x | +53 | +490x |
- class(self)[1],+ checkmate::assert_character(keys, any.missing = FALSE) |
98 | -6x | +54 | +490x |
- self$get_dataname(),+ checkmate::assert( |
99 | -6x | +55 | +490x |
- ifelse(self$is_pulled(), "", "not "),+ checkmate::check_character(code, max.len = 1, any.missing = FALSE), |
100 | -6x | +56 | +490x |
- ifelse(self$is_pulled(), ":", "")+ checkmate::check_class(code, "CodeClass") |
101 | +57 |
- ))+ ) |
||
102 | -6x | +|||
58 | +
- if (self$is_pulled()) {+ # label might be NULL also because of taking label attribute from data.frame - missing attr is NULL |
|||
103 | -2x | +59 | +490x |
- print(self$get_dataset())+ checkmate::assert_character(label, max.len = 1, null.ok = TRUE, any.missing = FALSE) |
104 | -+ | |||
60 | +490x |
- }+ checkmate::assert_list(vars, names = "named") |
||
105 | +61 | |||
106 | -6x | -
- invisible(self)- |
- ||
107 | -+ | 62 | +490x |
- },+ validate_metadata(metadata) |
108 | +63 | |||
109 | -+ | |||
64 | +487x |
- # ___ getters ====+ private$.raw_data <- x |
||
110 | -+ | |||
65 | +487x |
- #' @description+ private$metadata <- metadata |
||
111 | +66 |
- #' Get `dataname` of dataset+ |
||
112 | -+ | |||
67 | +487x |
- #'+ private$set_dataname(dataname) |
||
113 | -+ | |||
68 | +487x |
- #' @return `dataname` of the dataset+ self$set_vars(vars) |
||
114 | -+ | |||
69 | +487x |
- get_dataname = function() {+ self$set_dataset_label(label) |
||
115 | -522x | +70 | +487x |
- return(private$dataname)+ self$set_keys(keys) |
116 | +71 |
- },+ |
||
117 | +72 |
- #' @description+ # needed if recreating dataset - we need to preserve code order and uniqueness |
||
118 | -+ | |||
73 | +487x |
- #' Get `dataname` of dataset+ private$code <- CodeClass$new() |
||
119 | -+ | |||
74 | +487x |
- #'+ if (is.character(code)) { |
||
120 | -+ | |||
75 | +281x |
- #' @return `character` `dataname` of the dataset+ self$set_code(code) |
||
121 | +76 |
- get_datanames = function() {+ } else { |
||
122 | -11x | +77 | +206x |
- return(private$dataname)+ private$code$append(code) |
123 | +78 |
- },+ } |
||
124 | +79 |
- #' @description+ |
||
125 | -+ | |||
80 | +487x |
- #' Get label of dataset+ logger::log_trace("TealDataset initialized for dataset: { deparse1(self$get_dataname()) }.") |
||
126 | -+ | |||
81 | +487x |
- #'+ return(invisible(self)) |
||
127 | +82 |
- #' @return `character` dataset label+ }, |
||
128 | +83 |
- get_dataset_label = function() {- |
- ||
129 | -132x | -
- return(private$dataset_label)+ |
||
130 | +84 |
- },+ #' @description |
||
131 | +85 |
- #' @description+ #' Recreate this `TealDataset` with its current attributes. |
||
132 | +86 |
- #' Get primary keys of dataset+ #' |
||
133 | +87 |
- #' @return `character` vector with dataset primary keys+ #' @return a new object of the `TealDataset` class |
||
134 | +88 |
- get_keys = function() {- |
- ||
135 | -152x | -
- return(private$keys)+ recreate = function(dataname = self$get_dataname(), |
||
136 | +89 |
- },+ x = self$get_raw_data(), |
||
137 | +90 |
- #' @description+ keys = self$get_keys(), |
||
138 | +91 |
- #' Get code to get data+ code = private$code, |
||
139 | +92 |
- #'+ label = self$get_dataset_label(), |
||
140 | +93 |
- #' @param deparse (`logical`)\cr+ vars = list(), |
||
141 | +94 |
- #' whether return deparsed form of a call+ metadata = self$get_metadata()) { |
||
142 | -- |
- #'+ | ||
95 | +53x | +
+ res <- self$initialize( |
||
143 | -+ | |||
96 | +53x |
- #' @return optionally deparsed `call` object+ dataname = dataname,+ |
+ ||
97 | +53x | +
+ x = x,+ |
+ ||
98 | +53x | +
+ keys = keys,+ |
+ ||
99 | +53x | +
+ code = code,+ |
+ ||
100 | +53x | +
+ label = label,+ |
+ ||
101 | +53x | +
+ vars = vars,+ |
+ ||
102 | +53x | +
+ metadata = metadata |
||
144 | +103 |
- get_code = function(deparse = TRUE) {+ ) |
||
145 | -44x | +104 | +53x |
- checkmate::assert_flag(deparse)+ logger::log_trace("TealDataset$recreate recreated dataset: { deparse1(self$get_dataname()) }.") |
146 | -44x | +105 | +53x |
- return(self$get_code_class()$get_code(deparse = deparse))+ return(res) |
147 | +106 |
}, |
||
148 | +107 |
#' @description |
||
149 | +108 |
- #' Get internal `CodeClass` object+ #' Prints this `TealDataset`. |
||
150 | +109 |
#' |
||
151 | +110 |
- #' @return `CodeClass`+ #' @param ... additional arguments to the printing method |
||
152 | +111 |
- get_code_class = function() {+ #' @return invisibly self |
||
153 | -186x | +|||
112 | +
- code_class <- CodeClass$new()+ print = function(...) { |
|||
154 | -186x | +113 | +8x |
- pull_code_class <- private$get_pull_code_class()+ check_ellipsis(...) |
155 | -186x | +114 | +8x |
- code_class$append(pull_code_class)+ cat(sprintf( |
156 | -+ | |||
115 | +8x |
-
+ "A %s object containing the following data.frame (%s rows and %s columns):\n", |
||
157 | -186x | +116 | +8x |
- if (!is.null(private$dataset)) {+ class(self)[1], |
158 | -70x | +117 | +8x |
- executed_code_in_dataset <- private$dataset$get_code_class()+ self$get_nrow(), |
159 | -70x | +118 | +8x |
- code_class$append(executed_code_in_dataset)+ self$get_ncol() |
160 | +119 |
- }+ ))+ |
+ ||
120 | +8x | +
+ print(head(as.data.frame(self$get_raw_data())))+ |
+ ||
121 | +8x | +
+ if (self$get_nrow() > 6) {+ |
+ ||
122 | +1x | +
+ cat("...\n") |
||
161 | +123 |
-
+ } |
||
162 | -186x | +124 | +8x |
- return(code_class)+ invisible(self) |
163 | +125 |
}, |
||
164 | +126 | ++ |
+ # ___ getters ====+ |
+ |
127 |
#' @description |
|||
165 | +128 |
- #'+ #' Performs any delayed mutate calls before returning self. |
||
166 | +129 |
- #' Derive the arguments this connector will pull with+ #' |
||
167 | +130 |
- #' @return `list` of pull function fixed arguments+ #' @return dataset (`TealDataset`) |
||
168 | +131 |
- get_pull_args = function() {+ get_dataset = function() { |
||
169 | -! | +|||
132 | +228x |
- private$pull_callable$get_args()+ if (self$is_mutate_delayed() && !private$is_any_dependency_delayed()) {+ |
+ ||
133 | +2x | +
+ private$mutate_eager() |
||
170 | +134 |
- },+ }+ |
+ ||
135 | +228x | +
+ return(self) |
||
171 | +136 |
- #' @description+ }, |
||
172 | +137 |
- #' Get dataset+ #' @description |
||
173 | +138 |
- #'+ #' Get all dataset attributes |
||
174 | +139 |
- #' @return dataset (`TealDataset`)+ #' @return (named `list`) with dataset attributes |
||
175 | +140 |
- get_dataset = function() {+ get_attrs = function() { |
||
176 | -120x | +|||
141 | +! |
- if (!self$is_pulled()) {+ x <- append( |
||
177 | -21x | +|||
142 | +! |
- stop(+ attributes(self$get_raw_data()), |
||
178 | -21x | +|||
143 | +! |
- sprintf("'%s' has not been pulled yet\n - please use `load_dataset()` first.", self$get_dataname()),+ list( |
||
179 | -21x | +|||
144 | +! |
- call. = FALSE+ column_labels = self$get_column_labels(), |
||
180 | -+ | |||
145 | +! |
- )+ row_labels = self$get_row_labels(), |
||
181 | -+ | |||
146 | +! |
- }+ dataname = self$get_dataname(), |
||
182 | -99x | +|||
147 | +! |
- private$dataset$get_dataset()+ dataset_label = self$get_dataset_label(), |
||
183 | -99x | +|||
148 | +! |
- return(private$dataset)+ keys = self$get_keys() |
||
184 | +149 |
- },+ ) |
||
185 | +150 |
- #' @description+ )+ |
+ ||
151 | +! | +
+ return(x) |
||
186 | +152 |
- #' Get error message from last pull+ }, |
||
187 | +153 |
- #'+ #' @description |
||
188 | +154 |
- #' @return `character` object with error message or `character(0)` if last+ #' Derive the raw data frame inside this object |
||
189 | +155 |
- #' pull was successful.+ #' @return `data.frame` |
||
190 | +156 |
- get_error_message = function() {+ get_raw_data = function() { |
||
191 | -1x | +157 | +356x |
- return(private$pull_callable$get_error_message())+ private$.raw_data |
192 | +158 |
}, |
||
193 | +159 |
#' @description |
||
194 | -- |
- #' Get pull function- |
- ||
195 | +160 |
- #'+ #' Derive the names of all `numeric` columns |
||
196 | +161 |
- #' @return `CallableFunction`+ #' @return `character` vector. |
||
197 | +162 |
- get_pull_callable = function() {+ get_numeric_colnames = function() { |
||
198 | -28x | +163 | +1x |
- return(private$pull_callable)+ private$get_class_colnames("numeric") |
199 | +164 |
}, |
||
200 | +165 |
#' @description |
||
201 | -- |
- #' Get raw data from dataset- |
- ||
202 | +166 |
- #'+ #' Derive the names of all `character` columns |
||
203 | +167 |
- #' @return `data.frame` or `MultiAssayExperiment` data+ #' @return `character` vector. |
||
204 | +168 |
- get_raw_data = function() {- |
- ||
205 | -60x | -
- dataset <- self$get_dataset()+ get_character_colnames = function() { |
||
206 | -58x | +169 | +1x |
- return(dataset$get_raw_data())+ private$get_class_colnames("character") |
207 | +170 |
}, |
||
208 | +171 |
#' @description |
||
209 | -- |
- #' Get the list of dependencies that are `TealDataset` or `TealDatasetConnector` objects- |
- ||
210 | +172 |
- #'+ #' Derive the names of all `factor` columns |
||
211 | +173 |
- #' @return `list`+ #' @return `character` vector. |
||
212 | +174 |
- get_var_r6 = function() {+ get_factor_colnames = function() { |
||
213 | -47x | +175 | +1x |
- return(private$var_r6)+ private$get_class_colnames("factor") |
214 | +176 |
}, |
||
215 | +177 |
-
+ #' @description |
||
216 | +178 |
- # ___ setters ====+ #' Derive the column names |
||
217 | +179 |
- #' @description+ #' @return `character` vector. |
||
218 | +180 |
- #' Reassign `vars` in this object to keep references up to date after deep clone.+ get_colnames = function() { |
||
219 | -+ | |||
181 | +128x |
- #' Update is done based on the objects passed in `datasets` argument. Reassignment+ colnames(private$.raw_data) |
||
220 | +182 |
- #' refers only to the provided `datasets`, other `vars` remains the same.+ }, |
||
221 | +183 |
- #' @param datasets (`named list` of `TealDataset(s)` or `TealDatasetConnector(s)`)\cr+ #' @description |
||
222 | +184 |
- #' objects with valid pointers.+ #' Derive the column labels |
||
223 | +185 |
- #' @return NULL invisible+ #' @return `character` vector. |
||
224 | +186 |
- reassign_datasets_vars = function(datasets) {+ get_column_labels = function() { |
||
225 | -7x | +187 | +1x |
- logger::log_trace(+ col_labels(private$.raw_data, fill = FALSE) |
226 | -7x | +|||
188 | +
- "TealDatasetConnector$reassign_datasets_vars reassigning vars in dataset: { self$get_dataname() }."+ }, |
|||
227 | +189 |
- )+ #' @description |
||
228 | -7x | -
- checkmate::assert_list(datasets, min.len = 0, names = "unique")+ | ||
190 | ++ |
+ #' Get the number of columns of the data |
||
229 | +191 |
-
+ #' @return `numeric` vector |
||
230 | -7x | +|||
192 | +
- common_var_r6 <- intersect(names(datasets), names(private$var_r6))+ get_ncol = function() { |
|||
231 | -7x | +193 | +9x |
- private$var_r6[common_var_r6] <- datasets[common_var_r6]+ ncol(private$.raw_data) |
232 | +194 |
-
+ }, |
||
233 | -7x | +|||
195 | +
- common_vars <- intersect(names(datasets), names(private$pull_vars))+ #' @description |
|||
234 | -7x | +|||
196 | +
- private$pull_vars[common_vars] <- datasets[common_vars]+ #' Get the number of rows of the data |
|||
235 | +197 |
-
+ #' @return `numeric` vector |
||
236 | -7x | +|||
198 | +
- if (!is.null(private$dataset)) {+ get_nrow = function() { |
|||
237 | -! | +|||
199 | +17x |
- private$dataset$reassign_datasets_vars(datasets)+ nrow(private$.raw_data) |
||
238 | +200 |
- }+ }, |
||
239 | -7x | +|||
201 | +
- logger::log_trace(+ #' @description |
|||
240 | -7x | +|||
202 | +
- "TealDatasetConnector$reassign_datasets_vars reassigned vars in dataset: { self$get_dataname() }."+ #' Derive the row names |
|||
241 | +203 |
- )+ #' @return `character` vector. |
||
242 | +204 |
-
+ get_rownames = function() { |
||
243 | -7x | +205 | +2x |
- invisible(NULL)+ rownames(private$.raw_data) |
244 | +206 |
}, |
||
245 | +207 |
#' @description |
||
246 | -- |
- #' Set label of the `dataset` object- |
- ||
247 | +208 |
- #'+ #' Derive the row labels |
||
248 | +209 |
- #' @return (`self`) invisibly for chaining+ #' @return `character` vector. |
||
249 | +210 |
- set_dataset_label = function(label) {+ get_row_labels = function() { |
||
250 | -182x | +211 | +1x |
- if (is.null(label)) {+ c() |
251 | -! | +|||
212 | +
- label <- character(0)+ }, |
|||
252 | +213 |
- }+ #' @description |
||
253 | -182x | +|||
214 | +
- checkmate::assert_character(label, max.len = 1, any.missing = FALSE)+ #' Derive the `name` which was formerly called `dataname` |
|||
254 | -182x | +|||
215 | +
- private$dataset_label <- label+ #' @return `character` name of the dataset |
|||
255 | -182x | +|||
216 | +
- if (self$is_pulled()) {+ get_dataname = function() { |
|||
256 | -! | +|||
217 | +1257x |
- private$dataset$set_dataset_label(label)+ private$dataname |
||
257 | +218 |
- }+ }, |
||
258 | -182x | +|||
219 | +
- logger::log_trace(+ #' @description |
|||
259 | -182x | +|||
220 | +
- "TealDatasetConnector$set_dataset_label label set for dataset: { deparse1(self$get_dataname()) }."+ #' Derive the `dataname` |
|||
260 | +221 |
- )+ #' @return `character` name of the dataset |
||
261 | +222 |
-
+ get_datanames = function() { |
||
262 | -182x | +223 | +159x |
- return(invisible(self))+ private$dataname |
263 | +224 |
}, |
||
264 | +225 |
#' @description |
||
265 | +226 |
- #' Set new keys+ #' Derive the `label` which was former called `datalabel` |
||
266 | +227 |
- #' @return (`self`) invisibly for chaining.+ #' @return `character` label of the dataset |
||
267 | +228 |
- set_keys = function(keys) {+ get_dataset_label = function() { |
||
268 | -182x | +229 | +93x |
- checkmate::assert_character(keys, any.missing = FALSE)+ private$dataset_label |
269 | -182x | +|||
230 | +
- if (isTRUE(self$is_pulled())) {+ }, |
|||
270 | -! | +|||
231 | +
- set_keys(private$dataset, keys)+ #' @description |
|||
271 | +232 |
- }+ #' Get primary keys of dataset |
||
272 | -182x | +|||
233 | +
- private$keys <- keys+ #' @return (`character` vector) with dataset primary keys+ |
+ |||
234 | ++ |
+ get_keys = function() { |
||
273 | -182x | +235 | +208x |
- logger::log_trace("TealDatasetConnector$set_keys keys set for dataset: { deparse1(self$get_dataname()) }.")+ private$.keys |
274 | +236 |
-
+ }, |
||
275 | -182x | +|||
237 | +
- return(invisible(self))+ #' @description |
|||
276 | +238 |
- },+ #' Get metadata of dataset |
||
277 | +239 |
-
+ #' @return (named `list`) |
||
278 | +240 |
- # ___ pull ====+ get_metadata = function() {+ |
+ ||
241 | +100x | +
+ private$metadata |
||
279 | +242 | ++ |
+ },+ |
+ |
243 |
#' @description |
|||
280 | +244 |
- #' Pull the data (and metadata if it is a `Callable`)+ #' Get the list of dependencies that are `TealDataset` or `TealDatasetConnector` objects |
||
281 | +245 |
#' |
||
282 | +246 |
- #' Read or create data using `pull_callable` specified in the constructor.+ #' @return `list` |
||
283 | +247 |
- #'+ get_var_r6 = function() {+ |
+ ||
248 | +105x | +
+ return(private$var_r6) |
||
284 | +249 |
- #' @param args (`NULL` or named `list`)\cr+ }, |
||
285 | +250 |
- #' additional dynamic arguments for pull function. `args` can be omitted if `pull_callable`+ # ___ setters ==== |
||
286 | +251 |
- #' from constructor already contains all necessary arguments to pull data. One can try+ #' @description |
||
287 | +252 |
- #' to execute `pull_callable` directly by `x$pull_callable$run()` or to get code using+ #' Overwrites `TealDataset` or `TealDatasetConnector` dependencies of this `TealDataset` with |
||
288 | +253 |
- #' `x$pull_callable$get_code()`. `args` specified in pull are used temporary to get data but+ #' those found in `datasets`. Reassignment |
||
289 | +254 |
- #' not saved in code.+ #' refers only to the provided `datasets`, other `vars` remains the same. |
||
290 | +255 |
- #' @param try (`logical` value)\cr+ #' @details |
||
291 | +256 |
- #' whether perform function evaluation inside `try` clause+ #' Reassign `vars` in this object to keep references up to date after deep clone. |
||
292 | +257 |
- #'+ #' Update is done based on the objects passed in `datasets` argument. |
||
293 | +258 |
- #' @return (`self`) if successful.+ #' Overwrites dependencies with names matching the names of the objects passed |
||
294 | +259 |
- pull = function(args = NULL, try = FALSE) {+ #' in `datasets`. |
||
295 | -118x | +|||
260 | +
- logger::log_trace("TealDatasetConnector$pull pulling dataset: {self$get_dataname() }.")+ #' @param datasets (`named list` of `TealDataset(s)` or `TealDatasetConnector(s)`)\cr |
|||
296 | -118x | +|||
261 | +
- data <- private$pull_internal(args = args, try = try)+ #' objects with valid pointers. |
|||
297 | -116x | +|||
262 | +
- if (!self$is_failed()) {+ #' @return NULL invisible |
|||
298 | +263 |
- # The first time object is pulled, private$dataset may be NULL if mutate method was never called+ #' @examples |
||
299 | -115x | +|||
264 | +
- has_dataset <- !is.null(private$dataset)+ #' test_dataset <- teal.data:::TealDataset$new( |
|||
300 | -115x | +|||
265 | +
- if (has_dataset) {+ #' dataname = "iris", |
|||
301 | -13x | +|||
266 | +
- code_in_dataset <- private$dataset$get_code_class(nodeps = TRUE)+ #' x = iris, |
|||
302 | -13x | +|||
267 | +
- vars_in_dataset <- private$dataset$get_vars()+ #' vars = list(dep = teal.data:::TealDataset$new("iris2", iris)) |
|||
303 | +268 |
- }+ #' ) |
||
304 | +269 |
-
+ #' test_dataset$reassign_datasets_vars( |
||
305 | -115x | +|||
270 | +
- pulled_metadata <- private$pull_metadata_internal()+ #' list(iris2 = teal.data:::TealDataset$new("iris2", head(iris))) |
|||
306 | -115x | +|||
271 | +
- private$dataset <- dataset(+ #' ) |
|||
307 | -115x | +|||
272 | +
- dataname = self$get_dataname(),+ #' |
|||
308 | -115x | +|||
273 | +
- x = data,+ reassign_datasets_vars = function(datasets) { |
|||
309 | -115x | +274 | +7x |
- keys = character(0), # keys need to be set after mutate+ checkmate::assert_list(datasets, min.len = 0, names = "unique") |
310 | -115x | +|||
275 | +
- label = self$get_dataset_label(),+ |
|||
311 | -115x | +276 | +7x |
- code = private$get_pull_code_class(),+ common_var_r6 <- intersect(names(datasets), names(private$var_r6)) |
312 | -115x | +277 | +7x |
- metadata = pulled_metadata+ private$var_r6[common_var_r6] <- datasets[common_var_r6] |
313 | +278 |
- )+ |
||
314 | -+ | |||
279 | +7x |
-
+ common_vars <- intersect(names(datasets), names(private$vars)) |
||
315 | -115x | +280 | +7x |
- if (has_dataset) {+ private$vars[common_vars] <- datasets[common_vars] |
316 | -13x | +|||
281 | +
- private$dataset$mutate(+ |
|||
317 | -13x | +282 | +7x |
- code = code_in_dataset,+ common_mutate_vars <- intersect(names(datasets), names(private$mutate_vars)) |
318 | -13x | +283 | +7x |
- vars = vars_in_dataset+ private$mutate_vars[common_mutate_vars] <- datasets[common_mutate_vars] |
319 | +284 |
- )+ + |
+ ||
285 | +7x | +
+ logger::log_trace(+ |
+ ||
286 | +7x | +
+ "TealDataset$reassign_datasets_vars reassigned vars for dataset: { deparse1(self$get_dataname()) }." |
||
320 | +287 |
- }+ ) |
||
321 | -115x | +288 | +7x |
- set_keys(private$dataset, self$get_keys())+ invisible(NULL) |
322 | -115x | +|||
289 | +
- private$is_pulled_flag <- TRUE+ }, |
|||
323 | -115x | +|||
290 | +
- logger::log_trace("TealDatasetConnector$pull pulled dataset: {self$get_dataname() }.")+ #' @description |
|||
324 | +291 |
- } else {+ #' Set the label for the dataset+ |
+ ||
292 | ++ |
+ #' @return (`self`) invisibly for chaining+ |
+ ||
293 | ++ |
+ set_dataset_label = function(label) { |
||
325 | -1x | +294 | +506x |
- logger::log_error("TealDatasetConnector$pull failed to pull dataset: {self$get_dataname() }.")+ if (is.null(label)) {+ |
+
295 | +166x | +
+ label <- character(0) |
||
326 | +296 |
} |
||
297 | +506x | +
+ checkmate::assert_character(label, max.len = 1, any.missing = FALSE)+ |
+ ||
298 | +506x | +
+ private$dataset_label <- label+ |
+ ||
327 | +299 | |||
328 | -116x | +300 | +506x | +
+ logger::log_trace(+ |
+
301 | +506x | +
+ "TealDataset$set_dataset_label dataset_label set for dataset: { deparse1(self$get_dataname()) }."+ |
+ ||
302 | ++ |
+ )+ |
+ ||
303 | +506x |
return(invisible(self)) |
||
329 | +304 |
}, |
||
330 | +305 |
#' @description |
||
331 | +306 |
- #' Set arguments to the pulling function+ #' Set new keys |
||
332 | +307 |
- #'+ #' @return (`self`) invisibly for chaining. |
||
333 | +308 |
- #' @param args (`NULL` or named `list`) dynamic arguments to function+ set_keys = function(keys) { |
||
334 | -+ | |||
309 | +625x |
- #'+ checkmate::assert_character(keys, any.missing = FALSE) |
||
335 | -+ | |||
310 | +625x |
- #' @return (`self`) invisibly for chaining+ private$.keys <- keys |
||
336 | -+ | |||
311 | +625x |
- set_args = function(args) {+ logger::log_trace(sprintf( |
||
337 | -1x | +312 | +625x |
- set_args(private$pull_callable, args)+ "TealDataset$set_keys set the keys %s for dataset: %s", |
338 | -1x | +313 | +625x |
- logger::log_trace("TealDatasetConnector$set_args pull args set for dataset: {self$get_dataname() }.")+ paste(keys, collapse = ", "), |
339 | -1x | +314 | +625x |
- return(invisible(self))+ self$get_dataname() |
340 | +315 |
- },+ ))+ |
+ ||
316 | +625x | +
+ return(invisible(self)) |
||
341 | +317 |
-
+ }, |
||
342 | +318 |
- # ___ mutate ====+ |
||
343 | +319 |
#' @description |
||
344 | +320 |
- #' Dispatcher for either eager or delayed mutate methods+ #' Adds variables which code depends on |
||
345 | +321 |
#' |
||
346 | +322 |
- #' Either code or script must be provided, but not both.+ #' @param vars (`named list`) contains any R object which code depends on |
||
347 | +323 |
- #'+ #' @return (`self`) invisibly for chaining |
||
348 | +324 |
- #' @return (`self`) invisibly for chaining.+ set_vars = function(vars) { |
||
349 | -+ | |||
325 | +576x |
- mutate = function(code, vars = list()) {+ private$set_vars_internal(vars, is_mutate_vars = FALSE) |
||
350 | -46x | +326 | +572x |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ logger::log_trace("TealDataset$set_vars vars set for dataset: { deparse1(self$get_dataname()) }.") |
351 | +327 | |||
352 | -46x | +328 | +572x |
- if (is.null(private$dataset)) {+ return(invisible(NULL)) |
353 | +329 |
- # just needs a dummy TealDataset object to store mutate code, hence col = 1+ }, |
||
354 | -11x | +|||
330 | +
- private$dataset <- TealDataset$new(dataname = self$get_dataname(), x = data.frame(col = 1))+ #' @description |
|||
355 | +331 |
- }+ #' Sets reproducible code |
||
356 | -46x | +|||
332 | +
- private$dataset$mutate(code = code, vars = vars, force_delay = !self$is_pulled())+ #' |
|||
357 | +333 |
- # should be called at the end so that failure in TealDataset object will prevent it.+ #' @return (`self`) invisibly for chaining |
||
358 | -45x | +|||
334 | +
- private$set_var_r6(vars)+ set_code = function(code) { |
|||
359 | -45x | +335 | +300x |
- logger::log_trace(+ checkmate::assert_character(code, max.len = 1, any.missing = FALSE) |
360 | -45x | +336 | +300x |
- sprintf(+ if (length(code) > 0 && code != "") { |
361 | -45x | +337 | +120x |
- "TealDatasetConnector$mutate mutated dataset '%s' using the code (%s lines) and vars (%s).",+ private$code$set_code( |
362 | -45x | +338 | +120x |
- self$get_dataname(),+ code = code, |
363 | -45x | +339 | +120x |
- length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code)),+ dataname = self$get_datanames(), |
364 | -45x | +340 | +120x |
- paste(names(vars), collapse = ", ")+ deps = names(private$vars) |
365 | +341 |
) |
||
366 | -- |
- )- |
- ||
367 | +342 |
-
+ } |
||
368 | -+ | |||
343 | +300x |
-
+ logger::log_trace("TealDataset$set_code code set for dataset: { deparse1(self$get_dataname()) }.") |
||
369 | -45x | +344 | +300x |
- return(invisible(self))+ return(invisible(NULL)) |
370 | +345 |
}, |
||
371 | +346 | |||
372 | +347 |
- # ___ status ====+ # ___ get_code ==== |
||
373 | +348 |
#' @description |
||
374 | +349 |
- #' Check if pull has not failed.+ #' Get code to get data |
||
375 | +350 |
#' |
||
376 | +351 |
- #' @return `TRUE` if pull failed, else `FALSE`+ #' @param deparse (`logical`) whether return deparsed form of a call |
||
377 | +352 |
- is_failed = function() {- |
- ||
378 | -143x | -
- return(private$pull_callable$is_failed())+ #' |
||
379 | +353 |
- },+ #' @return optionally deparsed `call` object |
||
380 | +354 |
- #' @description+ get_code = function(deparse = TRUE) { |
||
381 | -+ | |||
355 | +60x |
- #' Check if dataset has already been pulled.+ checkmate::assert_flag(deparse) |
||
382 | -+ | |||
356 | +60x |
- #'+ res <- self$get_code_class()$get_code(deparse = deparse) |
||
383 | -+ | |||
357 | +60x |
- #' @return `TRUE` if connector has been already pulled, else `FALSE`+ return(res) |
||
384 | +358 |
- is_pulled = function() {+ }, |
||
385 | -680x | +|||
359 | +
- private$is_pulled_flag+ #' @description |
|||
386 | +360 |
- },+ #' Get internal `CodeClass` object |
||
387 | +361 |
- #' @description+ #' @param nodeps (`logical(1)`) whether `CodeClass` should not contain the code |
||
388 | +362 |
- #' Check if dataset has mutations that are delayed+ #' of the dependent `vars` |
||
389 | +363 |
- #'+ #' the `mutate` |
||
390 | +364 |
- #' @return `logical`+ #' @return `CodeClass` |
||
391 | +365 |
- is_mutate_delayed = function() {+ get_code_class = function(nodeps = FALSE) { |
||
392 | -58x | +366 | +383x |
- if (is.null(private$dataset)) {+ res <- CodeClass$new()+ |
+
367 | ++ |
+ # precise order matters |
||
393 | -2x | +368 | +383x |
- FALSE+ if (!nodeps) { |
394 | -+ | |||
369 | +370x |
- } else {+ res$append(list_to_code_class(private$vars)) |
||
395 | -56x | +370 | +370x |
- private$dataset$is_mutate_delayed()+ res$append(list_to_code_class(private$mutate_vars)) |
396 | +371 |
} |
||
397 | -+ | |||
372 | +383x |
- },+ res$append(private$code) |
||
398 | -+ | |||
373 | +383x |
-
+ res$append(private$mutate_list_to_code_class()) |
||
399 | +374 |
- # ___ check ====+ + |
+ ||
375 | +383x | +
+ return(res) |
||
400 | +376 | ++ |
+ },+ |
+ |
377 | ++ |
+ #' @description+ |
+ ||
378 | ++ |
+ #' Get internal `CodeClass` object+ |
+ ||
379 | ++ |
+ #'+ |
+ ||
380 | ++ |
+ #' @return `CodeClass`+ |
+ ||
381 | ++ |
+ get_mutate_code_class = function() {+ |
+ ||
382 | +! | +
+ res <- CodeClass$new()+ |
+ ||
383 | +! | +
+ res$append(list_to_code_class(private$mutate_vars))+ |
+ ||
384 | +! | +
+ res$append(private$mutate_list_to_code_class())+ |
+ ||
385 | ++ | + + | +||
386 | +! | +
+ return(res)+ |
+ ||
387 | ++ |
+ },+ |
+ ||
388 | ++ |
+ #' @description+ |
+ ||
389 | ++ |
+ #' Get internal `vars` object+ |
+ ||
390 | ++ |
+ #'+ |
+ ||
391 | ++ |
+ #' @return `list`+ |
+ ||
392 | ++ |
+ get_vars = function() {+ |
+ ||
393 | +17x | +
+ return(c(+ |
+ ||
394 | +17x | +
+ private$vars,+ |
+ ||
395 | +17x | +
+ private$mutate_vars[!names(private$mutate_vars) %in% names(private$vars)]+ |
+ ||
396 | ++ |
+ ))+ |
+ ||
397 | ++ |
+ },+ |
+ ||
398 |
#' @description |
|||
399 | ++ |
+ #' Get internal `mutate_vars` object+ |
+ ||
400 | ++ |
+ #'+ |
+ ||
401 |
- #' Check to determine if the raw data is reproducible from the+ #' @return `list` |
|||
402 |
- #' `get_code()` code.+ get_mutate_vars = function() { |
|||
403 | -+ | 2x |
- #' @return+ return(private$mutate_vars) |
|
404 |
- #' `TRUE` always for all connectors to avoid evaluating the same code multiple times.+ }, |
|||
405 |
- check = function() {+ |
|||
406 | -10x | +
- return(TRUE)+ #' @description |
||
407 |
- },+ #' Whether mutate code has delayed evaluation. |
|||
408 |
- # ___ shiny ====+ #' @return `logical` |
|||
409 |
- #' @description+ is_mutate_delayed = function() { |
|||
410 | -+ | 348x |
- #' Sets the shiny UI according to the given inputs.+ return(length(private$mutate_code) > 0) |
|
411 |
- #' Inputs must provide only scalar (length of 1) variables.+ }, |
|||
412 |
- #' @param inputs (`function`) A shiny module UI function with single argument `ns`.+ |
|||
413 |
- #' This function needs to return a list of shiny inputs with their `inputId` wrapped+ # ___ mutate ==== |
|||
414 |
- #' in function `ns`. The `inputId` must match exactly the argument name to be set.+ #' @description |
|||
415 |
- #' See example.+ #' Mutate dataset by code |
|||
416 |
- #' Nested lists are not allowed.+ #' |
|||
417 |
- #' @return (`self`) invisibly for chaining.+ #' @param code (`CodeClass`) or (`character`) R expressions to be executed |
|||
418 |
- #' @examples+ #' @param vars a named list of R objects that `code` depends on to execute |
|||
419 |
- #' ds <- dataset_connector("xyz", pull_callable = callable_function(data.frame))+ #' @param force_delay (`logical`) used by the containing `TealDatasetConnector` object |
|||
420 |
- #' ds$set_ui_input(+ #' |
|||
421 |
- #' function(ns) {+ #' Either code or script must be provided, but not both. |
|||
422 |
- #' list(sliderInput(ns("colA"), "Select value for colA", min = 0, max = 10, value = 3),+ #' |
|||
423 |
- #' sliderInput(ns("colB"), "Select value for colB", min = 0, max = 10, value = 7))+ #' @return (`self`) invisibly for chaining |
|||
424 |
- #' }+ mutate = function(code, vars = list(), force_delay = FALSE) { |
|||
425 | -+ | 98x |
- #' )+ logger::log_trace( |
|
426 | -+ | 98x |
- #' \dontrun{+ sprintf( |
|
427 | -+ | 98x |
- #' ds$launch()+ "TealDatasetConnector$mutate mutating dataset '%s' using the code (%s lines) and vars (%s).", |
|
428 | -+ | 98x |
- #' }+ self$get_dataname(), |
|
429 | -+ | 98x |
- set_ui_input = function(inputs = NULL) {+ length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)), |
|
430 | -3x | +98x |
- stopifnot(is.null(inputs) || is.function(inputs))+ paste(names(vars), collapse = ", ") |
|
431 | -3x | +
- if (is.function(inputs)) {+ ) |
||
432 | -3x | +
- if (!identical(names(formals(inputs)), "ns")) {+ ) |
||
433 | -! | +
- stop("'inputs' must be a function of a single argument called 'ns'")+ |
||
434 | -+ | 98x |
- }+ checkmate::assert_flag(force_delay) |
|
435 | -+ | 98x |
- }+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
436 | -3x | +98x |
- private$ui_input <- inputs+ checkmate::assert( |
|
437 | -3x | +98x |
- logger::log_trace(+ checkmate::check_string(code), |
|
438 | -3x | +98x |
- "TealDatasetConnector$set_ui_input ui_input set for dataset: { deparse1(self$get_dataname()) }."+ checkmate::check_class(code, "CodeClass") |
|
440 | -3x | +
- return(invisible(self))+ |
||
441 | -+ | 97x |
- },+ if (inherits(code, "PythonCodeClass")) { |
|
442 | -+ | ! |
- #' @description+ self$set_vars(vars) |
|
443 | -+ | ! |
- #' Get shiny `ui` function+ self$set_code(code$get_code()) |
|
444 | -+ | ! |
- #' @param id (`character`) namespace id+ new_df <- code$eval(dataname = self$get_dataname()) |
|
445 |
- #' @return shiny UI in given namespace id+ |
|||
446 |
- get_ui = function(id) {+ # dataset is recreated by replacing data by mutated object |
|||
447 | -2x | +
- checkmate::assert_string(id)+ # mutation code is added to the code which replicates the data |
||
448 | -2x | +! |
- if (!is.null(private$ui)) {+ self$recreate( |
|
449 | -2x | +! |
- private$ui(id)+ x = new_df, |
|
450 | -+ | ! |
- }+ vars = list() |
|
451 |
- },+ ) |
|||
452 |
- #' @description+ } else { |
|||
453 | -+ | 97x |
- #' Get shiny server function+ private$mutate_delayed(code, vars) |
|
454 | -+ | 93x |
- #' @return shiny server function+ if (!(private$is_any_dependency_delayed(vars) || force_delay)) { |
|
455 | -+ | 58x |
- get_server = function() {+ private$mutate_eager() |
|
456 | -! | +
- return(private$server)+ } |
||
457 |
- },+ } |
|||
458 | -+ | 88x |
- #' @description+ logger::log_trace( |
|
459 | -+ | 88x |
- #' Launches a shiny app.+ sprintf( |
|
460 | -+ | 88x |
- #' @return Shiny app+ "TealDataset$mutate mutated dataset '%s' using the code (%s lines) and vars (%s).", |
|
461 | -+ | 88x |
- #' @examples+ self$get_dataname(), |
|
462 | -+ | 88x |
- #' ds <- dataset_connector("xyz", pull_callable = callable_function(data.frame))+ length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)), |
|
463 | -+ | 88x |
- #' ds$set_ui_input(+ paste(names(vars), collapse = ", ") |
|
464 |
- #' function(ns) {+ ) |
|||
465 |
- #' list(sliderInput(ns("colA"), "Select value for colA", min = 0, max = 10, value = 3),+ ) |
|||
466 |
- #' sliderInput(ns("colB"), "Select value for colB", min = 0, max = 10, value = 7))+ |
|||
467 | -+ | 88x |
- #' }+ return(invisible(self)) |
|
468 |
- #' )+ }, |
|||
469 |
- #' \dontrun{+ |
|||
470 |
- #' ds$launch()+ # ___ check ==== |
|||
471 |
- #' }+ #' @description |
|||
472 |
- launch = function() {+ #' Check to determine if the raw data is reproducible from the `get_code()` code. |
|||
473 | -! | +
- if (is.null(private$server)) {+ #' @return |
||
474 | -! | +
- stop("No arguments set yet. Please use set_ui_input method first.")+ #' `TRUE` if the dataset generated from evaluating the |
||
475 |
- }+ #' `get_code()` code is identical to the raw data, else `FALSE`. |
|||
476 | -! | +
- shinyApp(+ check = function() { |
||
477 | -! | +23x |
- ui = fluidPage(+ logger::log_trace( |
|
478 | -! | +23x |
- theme = get_teal_bs_theme(),+ "TealDataset$check executing the code to reproduce dataset: { deparse1(self$get_dataname()) }..." |
|
479 | -! | +
- self$get_ui(id = "main_app"),+ ) |
||
480 | -! | +23x |
- shinyjs::useShinyjs(),+ if (!checkmate::test_character(self$get_code(), len = 1, pattern = "\\w+")) { |
|
481 | -! | +2x |
- br(),+ stop( |
|
482 | -! | +2x |
- actionButton("pull", "Get data"),+ sprintf( |
|
483 | -! | +2x |
- br(),+ "Cannot check preprocessing code of '%s' - code is empty.", |
|
484 | -! | +2x |
- tableOutput("result")+ self$get_dataname() |
|
485 |
- ),+ ) |
|||
486 | -! | +
- server = function(input, output, session) {+ ) |
||
487 | -! | +
- session$onSessionEnded(stopApp)+ } |
||
488 | -! | +
- observeEvent(input$pull, {+ |
||
489 | -! | +21x |
- self$get_server()(id = "main_app")+ new_set <- private$execute_code( |
|
490 | -! | +21x |
- if (self$is_pulled()) {+ code = self$get_code_class(), |
|
491 | -! | +21x |
- output$result <- renderTable(head(self$get_raw_data()))+ vars = c( |
|
492 | -+ | 21x |
- }+ list(), # list() in the beginning to ensure c.list |
|
493 | -+ | 21x |
- })+ private$vars, |
|
494 | -+ | 21x |
- }+ setNames(list(self), self$get_dataname()) |
|
495 |
- )+ ) |
|||
496 |
- }+ ) |
|||
497 |
- ),+ |
|||
498 | -+ | 21x |
- ## __Private Fields ====+ res_check <- tryCatch( |
|
499 |
- private = list(+ { |
|||
500 | -+ | 21x |
- dataset = NULL, # TealDataset+ identical(self$get_raw_data(), new_set) |
|
501 |
- pull_callable = NULL, # Callable+ }, |
|||
502 | -+ | 21x |
- pull_vars = list(), # named list+ error = function(e) { |
|
503 | -+ | ! |
- dataname = character(0),+ FALSE |
|
504 |
- dataset_label = character(0),+ } |
|||
505 |
- metadata = NULL, # Callable or list+ ) |
|||
506 | -+ | 21x |
- keys = NULL,+ logger::log_trace("TealDataset$check { deparse1(self$get_dataname()) } reproducibility result: { res_check }.") |
|
507 |
- var_r6 = list(),+ |
|||
508 | -+ | 21x |
- ui_input = NULL, # NULL or list+ return(res_check) |
|
509 |
- is_pulled_flag = FALSE,+ }, |
|||
510 |
-
+ #' @description |
|||
511 |
- ## __Private Methods ====+ #' Check if keys has been specified correctly for dataset. Set of `keys` |
|||
512 |
- ui = function(id) {+ #' should distinguish unique rows or be `character(0)`. |
|||
513 | -2x | +
- ns <- NS(id)+ #' |
||
514 |
- # add namespace to input ids+ #' @return `TRUE` if dataset has been already pulled, else `FALSE` |
|||
515 | -2x | +
- ui <- if (!is.null(private$ui_input)) {+ check_keys = function(keys = private$.keys) { |
||
516 | -1x | +78x |
- do.call(private$ui_input, list(ns = ns))+ if (length(keys) > 0) { |
|
517 | -+ | 46x |
- } else {+ if (!all(keys %in% self$get_colnames())) { |
|
518 | -1x | +2x |
- NULL+ stop("Primary keys specifed for ", self$get_dataname(), " do not exist in the data.") |
|
519 |
- }+ } |
|||
520 |
- # check ui inputs+ |
|||
521 | -2x | +44x |
- if (!is.null(ui)) {+ duplicates <- get_key_duplicates(self$get_raw_data(), keys) |
|
522 | -1x | +44x |
- checkmate::assert_list(ui, types = "shiny.tag")+ if (nrow(duplicates) > 0) { |
|
523 | 1x |
- attr_class <- vapply(lapply(ui, "[[", i = "attribs"), "[[", character(1), i = "class")+ stop( |
||
524 | 1x |
- if (!all(grepl("shiny-input-container", attr_class))) {+ "Duplicate primary key values found in the dataset '", self$get_dataname(), "' :\n", |
||
525 | -! | +1x |
- stop("All elements must be shiny inputs")+ paste0(utils::capture.output(print(duplicates))[-c(1, 3)], collapse = "\n"), |
|
526 | -+ | 1x |
- }+ call. = FALSE |
|
527 |
- }+ ) |
|||
528 |
- # create ui+ } |
|||
529 | -2x | +
- if (!is.null(ui)) {+ } |
||
530 | -1x | +75x |
- tags$div(+ logger::log_trace("TealDataset$check_keys keys checking passed for dataset: { deparse1(self$get_dataname()) }.") |
|
531 | -1x | +
- tags$div(+ }, |
||
532 | -1x | +
- id = ns("inputs"),+ #' @description |
||
533 | -1x | +
- h4("TealDataset Connector for ", code(self$get_dataname())),+ #' Check if dataset has already been pulled. |
||
534 | -1x | +
- ui+ #' |
||
535 |
- )+ #' @return `TRUE` if dataset has been already pulled, else `FALSE` |
|||
536 |
- )+ is_pulled = function() { |
|||
537 | -+ | 174x |
- }+ return(TRUE) |
|
538 |
- },+ } |
|||
539 |
- server = function(id, data_args = NULL) {+ ), |
|||
540 | -! | +
- moduleServer(+ ## __Private Fields ==== |
||
541 | -! | +
- id = id,+ private = list( |
||
542 | -! | +
- function(input, output, session) {+ .raw_data = data.frame(), |
||
543 | -! | +
- withProgress(value = 1, message = paste("Pulling", self$get_dataname()), {+ metadata = NULL, |
||
544 |
- # set args to save them - args set will be returned in the call+ dataname = character(0), |
|||
545 | -! | +
- dataset_args <- if (!is.null(private$ui_input)) {+ code = NULL, # CodeClass after initialization |
||
546 | -! | +
- reactiveValuesToList(input)+ vars = list(), |
||
547 |
- } else {+ var_r6 = list(), |
|||
548 | -! | +
- NULL+ dataset_label = character(0), |
||
549 |
- }+ .keys = character(0), |
|||
550 | -! | +
- if (length(dataset_args) > 0) {+ mutate_code = list(), |
||
551 | -! | +
- self$set_args(args = dataset_args)+ mutate_vars = list(), |
||
552 |
- }+ |
|||
553 |
-
+ ## __Private Methods ==== |
|||
554 | -! | +
- self$pull(args = data_args, try = TRUE)+ mutate_delayed = function(code, vars) { |
||
555 | -+ | 97x |
-
+ private$set_vars_internal(vars, is_mutate_vars = TRUE) |
|
556 | -+ | 93x |
- # print error if any+ private$mutate_code[[length(private$mutate_code) + 1]] <- list(code = code, deps = names(vars)) |
|
557 | -+ | 93x |
- # error doesn't break an app+ logger::log_trace( |
|
558 | -! | +93x |
- if (self$is_failed()) {+ sprintf( |
|
559 | -! | +93x |
- shinyjs::alert(+ "TealDatasetConnector$mutate_delayed set the code (%s lines) and vars (%s) for dataset: %s.", |
|
560 | -! | +93x |
- sprintf(+ length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)), |
|
561 | -! | +93x |
- "Error pulling %s:\nError message: %s",+ paste(names(vars), collapse = ", "), |
|
562 | -! | +93x |
- self$get_dataname(),+ self$get_dataname() |
|
563 | -! | +
- self$get_error_message()+ ) |
||
564 |
- )+ ) |
|||
565 | -+ | 93x |
- )+ return(invisible(self)) |
|
566 |
- }+ }, |
|||
567 |
- })+ mutate_eager = function() { |
|||
568 | -+ | 60x |
- }+ logger::log_trace( |
|
569 | -+ | 60x |
- )+ "TealDatasetConnector$mutate_eager executing mutate code for dataset: { deparse1(self$get_dataname()) }..." |
|
570 | -! | +
- return(invisible(self))+ ) |
||
571 | -+ | 60x |
- },+ new_df <- private$execute_code( |
|
572 | -+ | 60x |
-
+ code = private$mutate_list_to_code_class(), |
|
573 | -+ | 60x |
- # need to have a custom deep_clone because one of the key fields are reference-type object+ vars = c( |
|
574 | -+ | 60x |
- # in particular: dataset is a R6 object that wouldn't be cloned using default clone(deep = T)+ list(), # list() in the beginning to ensure c.list |
|
575 | -+ | 60x |
- deep_clone = function(name, value) {+ private$vars, |
|
576 | -208x | +
- deep_clone_r6(name, value)+ # if they have the same name, then they are guaranteed to be identical objects. |
||
577 | -+ | 60x |
- },+ private$mutate_vars[!names(private$mutate_vars) %in% names(private$vars)], |
|
578 | -+ | 60x |
- get_pull_code_class = function(args = NULL) {+ setNames(list(self), self$get_dataname()) |
|
579 | -301x | +
- res <- CodeClass$new()+ ) |
||
580 | -301x | +
- res$append(list_to_code_class(private$pull_vars))+ ) |
||
581 | -301x | +
- code <- if (inherits(private$pull_callable, "CallableCode")) {+ |
||
582 | -21x | +
- tmp <- private$pull_callable$get_call(deparse = FALSE)+ # code set after successful evaluation |
||
583 | -21x | +
- tmp[[length(tmp)]] <- substitute(a <- b, list(a = as.name(private$dataname), b = tmp[[length(tmp)]]))+ # otherwise code != dataset |
||
584 | -21x | +
- paste0(vapply(tmp, deparse1, character(1), collapse = "\n"), collapse = "\n")+ # private$code$append(private$mutate_code) # nolint |
||
585 | -+ | 55x |
- } else {+ private$append_mutate_code() |
|
586 | -280x | +55x |
- deparse1(substitute(+ self$set_vars(private$mutate_vars) |
|
587 | -280x | +55x |
- a <- b,+ private$mutate_code <- list() |
|
588 | -280x | +55x |
- list(+ private$mutate_vars <- list() |
|
589 | -280x | +
- a = as.name(private$dataname),+ |
||
590 | -280x | +
- b = private$pull_callable$get_call(deparse = FALSE, args = args)+ # dataset is recreated by replacing data by mutated object |
||
591 |
- )+ # mutation code is added to the code which replicates the data |
|||
592 | -280x | +
- ), collapse = "\n")+ # because new_code contains also code of the |
||
593 | -+ | 55x |
- }+ new_self <- self$recreate( |
|
594 | -+ | 55x |
-
+ x = new_df, |
|
595 | -301x | +55x |
- res$set_code(code = code, dataname = private$dataname, deps = names(private$pull_vars))+ vars = list() |
|
596 | -301x | +
- return(res)+ ) |
||
597 |
- },+ |
|||
598 | -+ | 55x |
- set_pull_callable = function(pull_callable) {+ logger::log_trace( |
|
599 | -182x | +55x |
- stopifnot(inherits(pull_callable, "Callable"))+ "TealDatasetConnector$mutate_eager executed mutate code for dataset: { deparse1(self$get_dataname()) }." |
|
600 | -182x | +
- private$pull_callable <- pull_callable+ ) |
||
601 | -182x | +
- return(invisible(self))+ |
||
602 | -+ | 55x |
- },+ new_self |
|
603 |
- set_metadata = function(metadata) {+ }, |
|||
604 | -182x | +
- if (inherits(metadata, "Callable")) {+ |
||
605 | -4x | +
- private$metadata <- metadata+ # need to have a custom deep_clone because one of the key fields are reference-type object |
||
606 |
- } else {+ # in particular: code is a R6 object that wouldn't be cloned using default clone(deep = T) |
|||
607 | -178x | +
- validate_metadata(metadata)+ deep_clone = function(name, value) { |
||
608 | -178x | +1044x |
- private$metadata <- metadata+ deep_clone_r6(name, value) |
|
609 |
- }+ }, |
|||
610 | -182x | +
- return(invisible(self))+ get_class_colnames = function(class_type = "character") { |
||
611 | -+ | 3x |
- },+ checkmate::assert_string(class_type) |
|
612 | -+ | 3x |
- set_pull_vars = function(pull_vars) {+ return_cols <- self$get_colnames()[which(vapply( |
|
613 | -182x | +3x |
- checkmate::assert_list(pull_vars, min.len = 0, names = "unique")+ lapply(self$get_raw_data(), class), |
|
614 | -182x | +3x |
- private$pull_vars <- pull_vars+ function(x, target_class_name) any(x %in% target_class_name), |
|
615 | -182x | +3x |
- return(invisible(self))+ logical(1), |
|
616 | -+ | 3x |
- },+ target_class_name = class_type |
|
617 |
- pull_metadata_internal = function() {+ ))] |
|||
618 | -115x | +
- if (!checkmate::test_class(private$metadata, "Callable")) {+ |
||
619 | -112x | +3x |
- return(private$metadata)+ return(return_cols) |
|
620 |
- }+ }, |
|||
621 |
-
+ mutate_list_to_code_class = function() { |
|||
622 | -3x | +443x |
- logger::log_trace("TealDatasetConnector$pull pulling metadata for dataset: {self$get_dataname() }.")+ res <- CodeClass$new() |
|
623 | -3x | +443x |
- pulled_metadata <- private$metadata$run(try = TRUE)+ for (mutate_code in private$mutate_code) { |
|
624 | -+ | 121x |
-
+ if (inherits(mutate_code$code, "CodeClass")) { |
|
625 | -3x | +14x |
- if (checkmate::test_class(pulled_metadata, c("simpleError", "error"))) {+ res$append(mutate_code$code) |
|
626 | -1x | +
- logger::log_warn("TealDatasetConnector$pull pulling metadata failed for dataset: {self$get_dataname() }.")+ } else { |
||
627 | -1x | +107x |
- return(NULL)+ res$set_code( |
|
628 | -+ | 107x |
- }+ code = mutate_code$code, |
|
629 | -+ | 107x |
-
+ dataname = private$dataname, |
|
630 | -+ | 107x |
- # metadata pulled, now lets make sure it is valid+ deps = mutate_code$deps |
|
631 | -2x | +
- tryCatch(+ ) |
||
632 |
- {+ } |
|||
633 | -2x | +
- pulled_metadata <- as.list(pulled_metadata)+ } |
||
634 | -2x | +443x |
- validate_metadata(pulled_metadata)+ return(res) |
|
635 | -1x | +
- logger::log_trace("TealDatasetConnector$pull pulled metadata for dataset: {self$get_dataname() }.")+ }, |
||
636 | -1x | +
- return(pulled_metadata)+ append_mutate_code = function() { |
||
637 | -+ | 55x |
- },+ for (mutate_code in private$mutate_code) { |
|
638 | -2x | +57x |
- error = function(e) {+ if (inherits(mutate_code$code, "CodeClass")) { |
|
639 | -1x | +11x |
- logger::log_warn("TealDatasetConnector$pull invalid metadata for dataset: {self$get_dataname() }.")+ private$code$append(mutate_code$code) |
|
640 | -1x | +
- return(NULL)+ } else { |
||
641 | -+ | 46x |
- }+ private$code$set_code( |
|
642 | -+ | 46x |
- )+ code = mutate_code$code, |
|
643 | -+ | 46x |
- },+ dataname = private$dataname, |
|
644 | -+ | 46x |
- pull_internal = function(args = NULL, try = FALSE) {+ deps = mutate_code$deps |
|
645 |
- # include objects CallableFunction environment+ ) |
|||
646 | -118x | +
- if (length(private$pull_vars) > 0) {+ } |
||
647 | -57x | +
- for (var_idx in seq_along(private$pull_vars)) {+ } |
||
648 | -57x | +
- var_name <- names(private$pull_vars)[[var_idx]]+ }, |
||
649 | -57x | +
- var_value <- private$pull_vars[[var_idx]]+ is_any_dependency_delayed = function(vars = list()) { |
||
650 | -+ | 101x |
-
+ any(vapply( |
|
651 | -+ | 101x |
- # assignment is done in pull_callable only once+ c(list(), private$var_r6, vars), |
|
652 | -+ | 101x |
- # because x is locked within local environment+ FUN.VALUE = logical(1), |
|
653 | -+ | 101x |
- # this means that re-assignment is not possible and will be silently skipped+ FUN = function(var) { |
|
654 | -+ | 130x |
- # During the app loading, assign is called only once.+ if (inherits(var, "TealDatasetConnector")) { |
|
655 | -57x | +68x |
- private$pull_callable$assign_to_env(+ !var$is_pulled() || var$is_mutate_delayed() |
|
656 | -57x | +62x |
- x = var_name,+ } else if (inherits(var, "TealDataset")) { |
|
657 | -57x | +50x |
- value = if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) {+ var$is_mutate_delayed() |
|
658 | -48x | +
- get_raw_data(var_value)+ } else { |
||
659 | -+ | 12x |
- } else {+ FALSE |
|
660 | -! | +
- var_value+ } |
||
661 |
- }+ } |
|||
662 |
- )+ )) |
|||
663 |
- }+ }, |
|||
664 |
- }+ |
|||
665 |
- # eval CallableFunction with dynamic args+ # Set variables which code depends on |
|||
666 | -118x | +
- tryCatch(+ # @param vars (`named list`) contains any R object which code depends on |
||
667 | -118x | +
- expr = private$pull_callable$run(args = args, try = try),+ # @param is_mutate_vars (`logical(1)`) whether this var is used in mutate code |
||
668 | -118x | +
- error = function(e) {+ set_vars_internal = function(vars, is_mutate_vars = FALSE) { |
||
669 | -2x | +673x |
- if (grepl("object 'conn' not found", e$message)) {+ checkmate::assert_flag(is_mutate_vars) |
|
670 | -! | +673x |
- output_message <- "This dataset connector requires connection object (conn) to be provided."+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
671 |
- } else {+ |
|||
672 | -2x | +673x |
- output_message <- paste("Could not pull dataset, the following error message was returned:", e$message)+ total_vars <- c(list(), private$vars, private$mutate_vars) |
|
673 |
- }+ |
|||
674 | -2x | +673x |
- stop(output_message, call. = FALSE)+ if (length(vars) > 0) { |
|
675 |
- }+ # not allowing overriding variable names |
|||
676 | -+ | 89x |
- )+ over_rides <- names(vars)[vapply( |
|
677 | -+ | 89x |
- },+ names(vars), |
|
678 | -+ | 89x |
- set_failure = function(res) {+ FUN.VALUE = logical(1), |
|
679 | -! | +89x |
- if (inherits(res, "error")) {+ FUN = function(var_name) { |
|
680 | -! | +92x |
- private$failed <- TRUE+ var_name %in% names(total_vars) && |
|
681 | -! | +92x |
- private$failure_msg <- conditionMessage(res)+ !identical(total_vars[[var_name]], vars[[var_name]]) |
|
682 |
- } else {+ } |
|||
683 | -! | +
- private$failed <- FALSE+ )] |
||
684 | -! | +89x |
- private$failure_msg <- NULL+ if (length(over_rides) > 0) { |
|
685 | -+ | 2x |
- }+ stop(paste("Variable name(s) already used:", paste(over_rides, collapse = ", "))) |
|
686 | -! | +
- return(NULL)+ } |
||
687 | -+ | 87x |
- },+ if (is_mutate_vars) { |
|
688 | -+ | 44x |
- set_var_r6 = function(vars) {+ private$mutate_vars <- c( |
|
689 | -227x | +44x |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ private$mutate_vars[!names(private$mutate_vars) %in% names(vars)], |
|
690 | -227x | +44x |
- for (varname in names(vars)) {+ vars |
|
691 | -91x | +
- var <- vars[[varname]]+ ) |
||
692 |
-
+ } else { |
|||
693 | -91x | +43x |
- if (inherits(var, "TealDatasetConnector") || inherits(var, "TealDataset")) {+ private$vars <- c( |
|
694 | -80x | +43x |
- var_deps <- var$get_var_r6()+ private$vars[!names(private$vars) %in% names(vars)], |
|
695 | -80x | +43x |
- var_deps[[varname]] <- var+ vars |
|
696 | -80x | +
- for (var_dep_name in names(var_deps)) {+ ) |
||
697 | -85x | +
- var_dep <- var_deps[[var_dep_name]]+ } |
||
698 | -85x | +
- if (identical(self, var_dep)) {+ } |
||
699 | -! | +
- stop("Circular dependencies detected")+ # only adding dependencies if checks passed |
||
700 | -+ | 671x |
- }+ private$set_var_r6(vars) |
|
701 | -85x | +665x |
- private$var_r6[[var_dep_name]] <- var_dep+ return(invisible(NULL)) |
|
702 |
- }+ }, |
|||
703 |
- }+ |
|||
704 |
- }+ # Evaluate script code to modify data or to reproduce data |
|||
705 | -227x | +
- return(invisible(self))+ # |
||
706 |
- },+ # Evaluate script code to modify data or to reproduce data |
|||
707 |
- set_dataname = function(dataname) {+ # @param vars (named `list`) additional pre-requisite vars to execute code |
|||
708 | -182x | +
- checkmate::assert_string(dataname)+ # @return (`environment`) which stores modified `x` |
||
709 | -182x | +
- stopifnot(!grepl("\\s", dataname))+ execute_code = function(code, vars = list()) { |
||
710 | -182x | +81x |
- private$dataname <- dataname+ stopifnot(inherits(code, "CodeClass")) |
|
711 | -182x | +81x |
- return(invisible(self))+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
712 |
- },+ |
|||
713 | -+ | 81x |
- set_ui = function(ui_args = NULL) {+ execution_environment <- new.env(parent = parent.env(globalenv())) |
|
714 | -! | +
- private$ui <- function(id) {+ |
||
715 | -! | +
- ns <- NS(id)+ # set up environment for execution |
||
716 | -+ | 81x |
- # add namespace to input ids+ for (vars_idx in seq_along(vars)) { |
|
717 | -! | +126x |
- ui <- if (!is.null(ui_args)) {+ var_name <- names(vars)[[vars_idx]] |
|
718 | -! | +126x |
- do.call(ui_args, list(ns = ns))+ var_value <- vars[[vars_idx]] |
|
719 | -+ | 126x |
- } else {+ if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) { |
|
720 | -! | +106x |
- NULL+ var_value <- get_raw_data(var_value) |
|
722 | -+ | 126x |
- # check ui inputs+ assign(envir = execution_environment, x = var_name, value = var_value) |
|
723 | -! | +
- if (!is.null(ui)) {+ } |
||
724 | -! | +
- checkmate::assert_list(ui, types = "shiny.tag")+ |
||
725 | -! | +
- attr_class <- vapply(lapply(ui, "[[", i = "attribs"), "[[", character(1), i = "class")+ # execute |
||
726 | -! | +81x |
- if (!all(grepl("shiny-input-container", attr_class))) {+ code$eval(envir = execution_environment) |
|
727 | -! | +
- stop("All elements must be shiny inputs")+ |
||
728 | -+ | 77x |
- }+ if (!is.data.frame(execution_environment[[self$get_dataname()]])) { |
|
729 | -+ | 1x |
- }+ out_msg <- sprintf( |
|
730 | -+ | 1x |
- # create ui+ "\n%s\n\n - Code from %s need to return a data.frame assigned to an object of dataset name.", |
|
731 | -! | +1x |
- if (!is.null(ui)) {+ self$get_code(), |
|
732 | -! | +1x |
- tags$div(+ self$get_dataname() |
|
733 | -! | +
- tags$div(+ ) |
||
734 | -! | +
- id = ns("inputs"),+ |
||
735 | -! | +1x |
- h4("TealDataset Connector for ", code(self$get_dataname())),+ rlang::with_options( |
|
736 | -! | +1x |
- ui+ .expr = stop(out_msg, call. = FALSE), |
|
737 | -+ | 1x |
- )+ warning.length = max(min(8170, nchar(out_msg) + 30), 100) |
|
738 |
- )+ ) |
|||
739 |
- }+ } |
|||
740 |
- }+ |
|||
741 | -! | +76x |
- return(invisible(self))+ new_set <- execution_environment[[self$get_dataname()]] |
|
742 |
- }+ |
|||
743 | -+ | 76x |
- )+ return(new_set) |
|
744 |
- )+ }, |
1 | +745 |
- # TealDataConnector ------+ |
||
2 | +746 |
- #'+ # Set the name for the dataset |
||
3 | +747 |
- #'+ # @param `dataname` (`character`) the new name |
||
4 | +748 |
- #' @title Manage multiple and `TealDatasetConnector` of the same type.+ # @return self invisibly for chaining |
||
5 | +749 |
- #'+ set_dataname = function(dataname) { |
||
6 | -+ | |||
750 | +505x |
- #' @description `r lifecycle::badge("stable")`+ check_simple_name(dataname) |
||
7 | -+ | |||
751 | +505x |
- #' Class manages `TealDatasetConnector` to specify additional dynamic arguments and to+ private$dataname <- dataname |
||
8 | -+ | |||
752 | +505x |
- #' open/close connection.+ return(invisible(self)) |
||
9 | +753 |
- #'+ }, |
||
10 | +754 |
- #' @param connection (`TealDataConnection`)\cr+ set_var_r6 = function(vars) { |
||
11 | -+ | |||
755 | +671x |
- #' connection to data source+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
12 | -+ | |||
756 | +671x |
- #' @param connectors (`list` of `TealDatasetConnector` elements)\cr+ for (varname in names(vars)) { |
||
13 | -+ | |||
757 | +90x |
- #' list with dataset connectors+ var <- vars[[varname]] |
||
14 | +758 |
- #'+ |
||
15 | -+ | |||
759 | +90x |
- #' @examples+ if (inherits(var, "TealDatasetConnector") || inherits(var, "TealDataset")) { |
||
16 | -+ | |||
760 | +64x |
- #' library(magrittr)+ var_deps <- var$get_var_r6() |
||
17 | -+ | |||
761 | +64x |
- #'+ var_deps[[varname]] <- var |
||
18 | -+ | |||
762 | +64x |
- #' random_data_connector <- function(dataname) {+ for (var_dep_name in names(var_deps)) { |
||
19 | -+ | |||
763 | +82x |
- #' fun_dataset_connector(+ var_dep <- var_deps[[var_dep_name]] |
||
20 | -+ | |||
764 | +82x |
- #' dataname = dataname,+ if (identical(self, var_dep)) { |
||
21 | -+ | |||
765 | +6x |
- #' fun = teal.data::example_cdisc_data,+ stop("Circular dependencies detected") |
||
22 | +766 |
- #' fun_args = list(dataname = dataname),+ } |
||
23 | -+ | |||
767 | +76x |
- #' )+ private$var_r6[[var_dep_name]] <- var_dep |
||
24 | +768 |
- #' }+ } |
||
25 | +769 |
- #'+ } |
||
26 | +770 |
- #' open_fun <- callable_function(library)+ } |
||
27 | -+ | |||
771 | +665x |
- #' open_fun$set_args(list(package = "teal.data"))+ return(invisible(self)) |
||
28 | +772 |
- #'+ } |
||
29 | +773 |
- #' con <- data_connection(open_fun = open_fun)+ ), |
||
30 | +774 |
- #' con$set_open_server(+ ## __Active Fields ==== |
||
31 | +775 |
- #' function(id, connection) {+ active = list( |
||
32 | +776 |
- #' moduleServer(+ #' @field raw_data The data.frame behind this R6 class |
||
33 | +777 |
- #' id = id,+ raw_data = function() { |
||
34 | -+ | |||
778 | +37x |
- #' module = function(input, output, session) {+ private$.raw_data |
||
35 | +779 |
- #' connection$open(try = TRUE)+ }, |
||
36 | +780 |
- #' return(invisible(connection))+ #' @field data The data.frame behind this R6 class |
||
37 | +781 |
- #' }+ data = function() { |
||
38 | -+ | |||
782 | +40x |
- #' )+ private$.raw_data |
||
39 | +783 |
- #' }+ }, |
||
40 | +784 |
- #' )+ #' @field var_names The column names of the data |
||
41 | +785 |
- #'+ var_names = function() {+ |
+ ||
786 | +37x | +
+ colnames(private$.raw_data) |
||
42 | +787 |
- #' x <- teal.data:::TealDataConnector$new(+ } |
||
43 | +788 |
- #' connection = con,+ ) |
||
44 | +789 |
- #' connectors = list(+ ) |
||
45 | +790 |
- #' random_data_connector(dataname = "ADSL"),+ |
||
46 | +791 |
- #' random_data_connector(dataname = "ADLB")+ ## Constructors ==== |
||
47 | +792 |
- #' )+ |
||
48 | +793 |
- #' )+ #' Constructor for [`TealDataset`] class |
||
49 | +794 |
#' |
||
50 | +795 |
- #' x$set_ui(+ #' @description `r lifecycle::badge("stable")` |
||
51 | +796 |
- #' function(id, connection, connectors) {+ #' |
||
52 | +797 |
- #' ns <- NS(id)+ #' @param dataname (`character`) a given name for the dataset, it cannot contain spaces |
||
53 | +798 |
- #' tagList(+ #' |
||
54 | +799 |
- #' connection$get_open_ui(ns("open_connection")),+ #' @param x (`data.frame` or `MultiAssayExperiment`) object from which the dataset will be created |
||
55 | +800 |
- #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1),+ #' |
||
56 | +801 |
- #' do.call(+ #' @param keys optional, (`character`) vector with primary keys |
||
57 | +802 |
- #' what = "tagList",+ #' |
||
58 | +803 |
- #' args = lapply(+ #' @param code (`character`) a character string defining the code needed to |
||
59 | +804 |
- #' connectors,+ #' produce the data set in `x` |
||
60 | +805 |
- #' function(connector) {+ #' |
||
61 | +806 |
- #' div(+ #' @param label (`character`) label to describe the dataset |
||
62 | +807 |
- #' connector$get_ui(+ #' |
||
63 | +808 |
- #' id = ns(connector$get_dataname())+ #' @param vars (named `list`) in case when this object code depends on other `TealDataset` |
||
64 | +809 |
- #' ),+ #' object(s) or other constant value, this/these object(s) should be included as named |
||
65 | +810 |
- #' br()+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
66 | +811 |
- #' )+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
||
67 | +812 |
- #' }+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
68 | +813 |
- #' )+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
||
69 | +814 |
- #' )+ #' are included to this object as local `vars` and they cannot be modified |
||
70 | +815 |
- #' )+ #' within another dataset. |
||
71 | +816 |
- #' }+ #' |
||
72 | +817 |
- #' )+ #' @param metadata (named `list` or `NULL`) field containing metadata about the dataset. |
||
73 | +818 | ++ |
+ #' Each element of the list should be atomic and length one.+ |
+ |
819 |
#' |
|||
74 | +820 |
- #' x$set_server(+ #' @return [`TealDataset`] object |
||
75 | +821 |
- #' function(id, connection, connectors) {+ #' |
||
76 | +822 |
- #' moduleServer(+ #' @rdname dataset |
||
77 | +823 |
- #' id = id,+ #' |
||
78 | +824 |
- #' module = function(input, output, session) {+ #' @export |
||
79 | +825 |
- #' # opens connection+ #' |
||
80 | +826 |
- #' connection$get_open_server()(id = "open_connection", connection = connection)+ #' @examples |
||
81 | +827 |
- #' if (connection$is_opened()) {+ #' # Simple example |
||
82 | +828 |
- #' for (connector in connectors) {+ #' dataset("iris", iris) |
||
83 | +829 |
- #' set_args(connector, args = list(n = input$n))+ #' |
||
84 | +830 |
- #' # pull each dataset+ #' # Example with more arguments |
||
85 | +831 |
- #' connector$get_server()(id = connector$get_dataname())+ #' \dontrun{ |
||
86 | +832 |
- #' if (connector$is_failed()) {+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
87 | +833 |
- #' break+ #' ADSL_dataset <- dataset(dataname = "ADSL", x = ADSL) |
||
88 | +834 |
- #' }+ #' |
||
89 | +835 |
- #' }+ #' ADSL_dataset$get_dataname() |
||
90 | +836 |
- #' }+ #' |
||
91 | +837 |
- #' }+ #' ADSL_dataset <- dataset( |
||
92 | +838 |
- #' )+ #' dataname = "ADSL", |
||
93 | +839 |
- #' }+ #' x = ADSL, |
||
94 | +840 |
- #' )+ #' label = "AdAM subject-level dataset", |
||
95 | +841 |
- #' \dontrun{+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
||
96 | +842 |
- #' x$launch()+ #' ) |
||
97 | +843 |
- #' x$get_datasets()+ #' ADSL_dataset$get_metadata() |
||
98 | +844 |
- #' }+ #' ADSL_dataset$get_dataset_label() |
||
99 | +845 |
- TealDataConnector <- R6::R6Class( # nolint+ #' ADSL_dataset$get_code() |
||
100 | +846 |
- classname = "TealDataConnector",+ #' } |
||
101 | +847 |
- inherit = TealDataAbstract,+ dataset <- function(dataname, |
||
102 | +848 |
-
+ x, |
||
103 | +849 |
- ## __Public Methods ====+ keys = character(0), |
||
104 | +850 |
- public = list(+ label = data_label(x), |
||
105 | +851 |
- #' @description+ code = character(0), |
||
106 | +852 |
- #' Create a new `TealDataConnector` object+ vars = list(), |
||
107 | +853 |
- initialize = function(connection, connectors) {+ metadata = NULL) { |
||
108 | -18x | +854 | +258x |
- checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1)+ UseMethod("dataset", x) |
109 | +855 |
-
+ } |
||
110 | -18x | +|||
856 | +
- connectors_names <- vapply(connectors, get_dataname, character(1))+ |
|||
111 | -18x | +|||
857 | +
- connectors <- setNames(connectors, connectors_names)+ #' @rdname dataset |
|||
112 | +858 |
-
+ #' @export |
||
113 | -18x | +|||
859 | +
- private$check_names(connectors_names)+ dataset.data.frame <- function(dataname, |
|||
114 | +860 |
-
+ x, |
||
115 | -18x | +|||
861 | +
- if (!missing(connection)) {+ keys = character(0), |
|||
116 | -18x | +|||
862 | +
- stopifnot(inherits(connection, "TealDataConnection"))+ label = data_label(x), |
|||
117 | -18x | +|||
863 | +
- private$connection <- connection+ code = character(0), |
|||
118 | +864 |
- }+ vars = list(), |
||
119 | +865 |
-
+ metadata = NULL) { |
||
120 | -18x | +866 | +254x |
- private$datasets <- connectors+ checkmate::assert_string(dataname) |
121 | -+ | |||
867 | +254x |
-
+ checkmate::assert_data_frame(x) |
||
122 | -18x | +868 | +254x |
- private$pull_code <- CodeClass$new()+ checkmate::assert( |
123 | -18x | +869 | +254x |
- private$mutate_code <- CodeClass$new()+ checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ |
+
870 | +254x | +
+ checkmate::check_class(code, "CodeClass") |
||
124 | +871 |
-
+ ) |
||
125 | -18x | +872 | +254x |
- self$id <- sample.int(1e11, 1, useHash = TRUE)+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
126 | +873 | |||
127 | -+ | |||
874 | +254x |
-
+ TealDataset$new( |
||
128 | -18x | +875 | +254x |
- logger::log_trace(+ dataname = dataname, |
129 | -18x | +876 | +254x |
- "TealDataConnector initialized with data: { paste(self$get_datanames(), collapse = ' ') }."+ x = x, |
130 | -+ | |||
877 | +254x |
- )+ keys = keys, |
||
131 | -18x | +878 | +254x |
- return(invisible(self))+ code = code, |
132 | -+ | |||
879 | +254x |
- },+ label = label,+ |
+ ||
880 | +254x | +
+ vars = vars,+ |
+ ||
881 | +254x | +
+ metadata = metadata |
||
133 | +882 |
- #' @description+ ) |
||
134 | +883 |
- #' Prints this `TealDataConnector`.+ } |
||
135 | +884 |
- #'+ |
||
136 | +885 |
- #' @param ... additional arguments to the printing method+ #' Load `TealDataset` object from a file |
||
137 | +886 |
- #' @return invisibly self+ #' |
||
138 | +887 |
- print = function(...) {+ #' @description `r lifecycle::badge("experimental")` |
||
139 | -1x | +|||
888 | +
- check_ellipsis(...)+ #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise. |
|||
140 | +889 |
-
+ #' |
||
141 | -1x | +|||
890 | +
- cat(sprintf(+ #' @param path (`character`) string giving the pathname of the file to read from. |
|||
142 | -1x | +|||
891 | +
- "A currently %s %s object containing %d TealDataset/TealDatasetConnector object(s) as element(s).\n",+ #' @param code (`character`) reproducible code to re-create object |
|||
143 | -1x | +|||
892 | +
- ifelse(self$get_connection()$is_opened(), "opened", "not yet opened"),+ #' |
|||
144 | -1x | +|||
893 | +
- class(self)[1],+ #' @return `TealDataset` object |
|||
145 | -1x | +|||
894 | +
- length(private$datasets)+ #' |
|||
146 | +895 |
- ))+ #' @export |
||
147 | -1x | +|||
896 | +
- cat(sprintf(+ #' |
|||
148 | -1x | +|||
897 | +
- "%d of which is/are loaded/pulled:\n",+ #' @examples |
|||
149 | -1x | +|||
898 | +
- sum(vapply(private$datasets, function(x) x$is_pulled(), FUN.VALUE = logical(1)))+ #' # simple example |
|||
150 | +899 |
- ))+ #' file_example <- tempfile(fileext = ".R") |
||
151 | +900 |
-
+ #' writeLines( |
||
152 | -1x | +|||
901 | +
- for (i in seq_along(private$datasets)) {+ #' text = c( |
|||
153 | -2x | +|||
902 | +
- cat(sprintf("--> Element %d:\n", i))+ #' "library(teal.data) |
|||
154 | -2x | +|||
903 | +
- print(private$datasets[[i]])+ #' dataset(dataname = \"iris\", |
|||
155 | +904 |
- }+ #' x = iris, |
||
156 | +905 |
-
+ #' code = \"iris\")" |
||
157 | -1x | +|||
906 | +
- invisible(self)+ #' ), |
|||
158 | +907 |
- },+ #' con = file_example |
||
159 | +908 |
-
+ #' ) |
||
160 | +909 |
- # ___ getters ====+ #' x <- dataset_file(file_example, code = character(0)) |
||
161 | +910 |
- #' @description+ #' get_code(x) |
||
162 | +911 |
- #' Get connection to data source+ #' |
||
163 | +912 |
- #'+ #' # custom code |
||
164 | +913 |
- #' @return connector's connection+ #' file_example <- tempfile(fileext = ".R") |
||
165 | +914 |
- get_connection = function() {+ #' writeLines( |
||
166 | -1x | +|||
915 | +
- return(private$connection)+ #' text = c( |
|||
167 | +916 |
- },+ #' "library(teal.data) |
||
168 | +917 |
- #' @description+ #' |
||
169 | +918 |
- #' Get internal `CodeClass` object+ #' # code> |
||
170 | +919 |
- #'+ #' x <- iris |
||
171 | +920 |
- #' @return `CodeClass`+ #' x$a1 <- 1 |
||
172 | +921 |
- get_code_class = function() {+ #' x$a2 <- 2 |
||
173 | -30x | +|||
922 | +
- all_code <- CodeClass$new()+ #' |
|||
174 | +923 |
-
+ #' # <code |
||
175 | -30x | +|||
924 | +
- open_connection_code <- if (!is.null(private$connection)) {+ #' dataset(dataname = \"iris_mod\", x = x)" |
|||
176 | -30x | +|||
925 | +
- private$connection$get_open_call(deparse = TRUE)+ #' ), |
|||
177 | +926 |
- } else {+ #' con = file_example |
||
178 | -! | +|||
927 | +
- NULL+ #' ) |
|||
179 | +928 |
- }+ #' x <- dataset_file(file_example) |
||
180 | +929 |
-
+ #' get_code(x) |
||
181 | -30x | +|||
930 | +
- if (!is.null(open_connection_code)) {+ dataset_file <- function(path, code = get_code(path)) { |
|||
182 | -30x | +931 | +2x |
- all_code$set_code(open_connection_code, dataname = "*open")+ object <- object_file(path, "TealDataset") |
183 | -+ | |||
932 | +1x |
- }+ object$set_code(code) |
||
184 | -30x | +933 | +1x |
- datasets_code_class <- private$get_datasets_code_class()+ return(object) |
185 | -30x | +|||
934 | +
- all_code$append(datasets_code_class)+ } |
186 | +1 |
-
+ ## CallableFunction ==== |
||
187 | -30x | +|||
2 | +
- close_connection_code <- if (!is.null(private$connection)) {+ #' |
|||
188 | -30x | +|||
3 | +
- private$connection$get_close_call(deparse = TRUE, silent = TRUE)+ #' @title A \code{CallableFunction} class of objects |
|||
189 | +4 |
- } else {+ #' |
||
190 | -! | +|||
5 | +
- NULL+ #' @description Object that stores a function name together with its arguments. |
|||
191 | +6 |
- }+ #' Methods are then available to get the function call and evaluate it. |
||
192 | +7 |
-
+ #' |
||
193 | -30x | +|||
8 | +
- if (!is.null(close_connection_code)) {+ #' @keywords internal |
|||
194 | -! | +|||
9 | +
- all_code$set_code(close_connection_code, dataname = "*close")+ #' |
|||
195 | +10 |
- }+ CallableFunction <- R6::R6Class( # nolint |
||
196 | +11 |
-
+ "CallableFunction", |
||
197 | -30x | +|||
12 | +
- mutate_code_class <- private$get_mutate_code_class()+ inherit = Callable, |
|||
198 | -30x | +|||
13 | +
- all_code$append(mutate_code_class)+ |
|||
199 | +14 |
-
+ ## __Public Methods ==== |
||
200 | -30x | +|||
15 | +
- return(all_code)+ public = list( |
|||
201 | +16 |
- },+ #' @description |
||
202 | +17 |
- #' @description get the server function+ #' Create a new \code{CallableFunction} object |
||
203 | +18 |
#' |
||
204 | +19 |
- #' @return the `server` function+ #' @param fun (\code{function})\cr |
||
205 | +20 |
- get_server = function() {+ #' function to be evaluated in class. |
||
206 | -2x | +|||
21 | +
- if (is.null(private$server)) {+ #' This is either a `function` object or its name as a string. |
|||
207 | -! | +|||
22 | +
- stop("No server function set yet. Please use set_server method first.")+ #' @param env (\code{environment})\cr |
|||
208 | +23 |
- }+ #' environment where function will be evaluated |
||
209 | -2x | +|||
24 | +
- function(id, connection = private$connection, connectors = private$datasets) {+ #' |
|||
210 | -! | +|||
25 | +
- rv <- reactiveVal(NULL)+ #' @return new \code{CallableFunction} object |
|||
211 | -! | +|||
26 | +
- moduleServer(+ initialize = function(fun, env = new.env(parent = parent.env(globalenv()))) { |
|||
212 | -! | +|||
27 | +211x |
- id = id,+ super$initialize(env = env) |
||
213 | -! | +|||
28 | +211x |
- module = function(input, output, session) {+ if (missing(fun)) { |
||
214 | -! | +|||
29 | +1x |
- private$server(id = "data_input", connection = connection, connectors = connectors)+ stop("A valid function name must be provided.") |
||
215 | +30 |
- }+ }+ |
+ ||
31 | +210x | +
+ if (!(checkmate::test_string(fun) || is.function(fun) || is.call(fun) || is.symbol(fun))) {+ |
+ ||
32 | +1x | +
+ stop("CallableFunction can be specified as character, symbol, call or function") |
||
216 | +33 |
- )+ } |
||
217 | +34 | |||
218 | -! | +|||
35 | +208x |
- if (self$is_pulled()) {+ fun_name <- private$get_callable_function(fun) |
||
219 | -! | +|||
36 | +203x |
- return(rv(TRUE))+ private$fun_name <- deparse1(fun_name, collapse = "\n") |
||
220 | +37 |
- } else {+ |
||
221 | -! | +|||
38 | +203x |
- return(rv(FALSE))+ private$refresh() |
||
222 | +39 |
- }+ |
||
223 | -+ | |||
40 | +203x |
- }+ logger::log_trace("CallableFunction initialized with function: { deparse1(private$fun_name) }.") |
||
224 | +41 |
- },+ |
||
225 | -+ | |||
42 | +203x |
- #' @description get the `preopen` server function+ return(invisible(self)) |
||
226 | +43 |
- #'+ }, |
||
227 | +44 |
- #' @return the `server` function+ #' @description |
||
228 | +45 |
- get_preopen_server = function() {+ #' get the arguments a function gets called with |
||
229 | -! | +|||
46 | +
- function(id, connection = private$connection) {+ #' |
|||
230 | -! | +|||
47 | +
- if (!is.null(private$preopen_server)) {+ #' @return arguments the function gets called with |
|||
231 | -! | +|||
48 | +
- moduleServer(+ get_args = function() { |
|||
232 | -! | +|||
49 | +3x |
- id = id,+ return(private$args) |
||
233 | -! | +|||
50 | +
- module = function(input, output, session) {+ }, |
|||
234 | -! | +|||
51 | +
- private$preopen_server(id = "data_input", connection = connection)+ #' @description |
|||
235 | +52 |
- }+ #' Get function call with substituted arguments in \code{args}. |
||
236 | +53 |
- )+ #' These arguments will not be stored in the object. |
||
237 | +54 |
- }+ #' |
||
238 | +55 |
- }+ #' @param deparse (\code{logical} value)\cr |
||
239 | +56 |
- },+ #' whether to return a deparsed version of call |
||
240 | +57 |
- #' @description+ #' @param args (\code{NULL} or named \code{list})\cr |
||
241 | +58 |
- #' Get Shiny module with inputs for all `TealDatasetConnector` objects+ #' dynamic arguments to function |
||
242 | +59 |
#' |
||
243 | +60 |
- #' @param id `character` shiny element id+ #' @return \code{call} or \code{character} depending on \code{deparse} argument |
||
244 | +61 |
- #'+ get_call = function(deparse = TRUE, args = NULL) { |
||
245 | -+ | |||
62 | +479x |
- #' @return the `ui` function+ checkmate::assert_flag(deparse)+ |
+ ||
63 | +479x | +
+ checkmate::assert_list(args, names = "strict", min.len = 0, null.ok = TRUE) |
||
246 | +64 |
- get_ui = function(id) {+ |
||
247 | -3x | +65 | +479x |
- if (is.null(private$ui)) {+ old_args <- private$args |
248 | -1x | +66 | +6x |
- stop("No UI set yet. Please use set_ui method first.")+ if (length(args) > 0) self$set_args(args) |
249 | +67 |
- }+ |
||
250 | -2x | +68 | +479x |
- x <- function(id, connection = private$connection, connectors = private$datasets) {+ res <- if (deparse) { |
251 | -2x | +69 | +28x |
- ns <- NS(id)+ deparse1(private$call, collapse = "\n") |
252 | -2x | +|||
70 | +
- tags$div(+ } else { |
|||
253 | -2x | +71 | +451x |
- h3("Data Connector for:", lapply(self$get_datanames(), code)),+ private$call |
254 | -2x | +|||
72 | +
- tags$div(+ }+ |
+ |||
73 | ++ | + + | +||
74 | ++ |
+ # set args back to default |
||
255 | -2x | +75 | +479x |
- id = ns("data_input"),+ if (length(args) > 0) { |
256 | -2x | +76 | +6x |
- private$ui(id = ns("data_input"), connection = connection, connectors = connectors)+ lapply(names(args), self$set_arg_value, NULL) |
257 | -+ | |||
77 | +6x |
- )+ self$set_args(old_args) |
||
258 | +78 |
- )+ } |
||
259 | +79 |
- }+ |
||
260 | -2x | +80 | +479x |
- x(id)+ return(res) |
261 | +81 |
}, |
||
262 | +82 |
-
+ #' @description |
||
263 | +83 |
- # ___ setters ====+ #' Set up function arguments |
||
264 | +84 |
- #' @description+ #' |
||
265 | +85 |
- #' Set argument to the `pull_fun`+ #' @param args (\code{NULL} or named \code{list})\cr |
||
266 | +86 |
- #'+ #' function arguments to be stored persistently in the object. Setting \code{args} doesn't |
||
267 | +87 |
- #' @param args (named `list`)\cr+ #' remove other \code{args}, only create new of modify previous of the same name. |
||
268 | +88 |
- #' arguments values as separate list elements named by argument name. These arguments+ #' To clean arguments specify \code{args = NULL}. |
||
269 | +89 |
- #' are passed to each dataset.+ #' |
||
270 | +90 |
- #'+ #' @return (`self`) invisibly for chaining. |
||
271 | +91 |
- #' @return nothing+ set_args = function(args) { |
||
272 | +92 |
- set_pull_args = function(args) {+ # remove args if empty |
||
273 | -1x | +93 | +94x |
- lapply(private$datasets, function(x) set_args(x, args))+ if (length(args) == 0) { |
274 | -1x | +94 | +6x |
- logger::log_trace("TealDataConnector$set_pull_args pull args set.")+ private$args <- NULL |
275 | -1x | +95 | +6x |
- return(invisible(NULL))+ private$refresh() |
276 | -+ | |||
96 | +6x |
- },+ return(invisible(self)) |
||
277 | +97 |
- #' @description+ } |
||
278 | -+ | |||
98 | +88x |
- #' Set connector UI function+ checkmate::assert_list(args, min.len = 0, names = "unique") |
||
279 | +99 |
- #'+ |
||
280 | -+ | |||
100 | +88x |
- #' @param f (`function`)\cr+ for (idx in seq_along(args)) { |
||
281 | -+ | |||
101 | +121x |
- #' shiny module as function. Inputs specified in this `ui` are passed to server module+ self$set_arg_value( |
||
282 | -+ | |||
102 | +121x |
- #' defined by `set_server` method.+ name = names(args)[[idx]], |
||
283 | -+ | |||
103 | +121x |
- #'+ value = args[[idx]] |
||
284 | +104 |
- #' @return nothing+ ) |
||
285 | +105 |
- set_ui = function(f) {- |
- ||
286 | -4x | -
- stopifnot(inherits(f, "function"))+ } |
||
287 | -4x | +106 | +88x |
- stopifnot("id" %in% names(formals(f)))+ logger::log_trace( |
288 | -4x | +107 | +88x |
- stopifnot(all(c("connection", "connectors") %in% names(formals(f))) || "..." %in% names(formals(f)))+ "CallableFunction$set_args args set for function: { deparse1(private$fun_name) }." |
289 | -4x | +|||
108 | +
- private$ui <- f+ ) |
|||
290 | -4x | +|||
109 | +
- logger::log_trace("TealDataConnector$set_ui ui set.")+ |
|||
291 | -4x | +110 | +88x |
- return(invisible(NULL))+ return(invisible(self)) |
292 | +111 |
}, |
||
293 | +112 |
#' @description |
||
294 | +113 |
- #' Set connector server function+ #' Set up single function argument with value |
||
295 | +114 |
#' |
||
296 | +115 |
- #' This function will be called after submit button will be hit. There is no possibility to+ #' @param name (\code{character}) argument name |
||
297 | +116 |
- #' specify some dynamic `ui` as `server` function is executed after hitting submit+ #' @param value argument value |
||
298 | +117 |
- #' button.+ #' |
||
299 | +118 |
- #'+ #' @return (`self`) invisibly for chaining. |
||
300 | +119 |
- #' @param f (`function`)\cr+ set_arg_value = function(name, value) { |
||
301 | -+ | |||
120 | +130x |
- #' A shiny module server function that should load data from all connectors+ checkmate::assert_string(name) |
||
302 | -+ | |||
121 | +130x |
- #'+ arg_names <- names(formals(eval(str2lang(private$fun_name)))) |
||
303 | -+ | |||
122 | +130x |
- #' @return nothing+ stopifnot(name %in% arg_names || "..." %in% arg_names || is.null(arg_names)) |
||
304 | +123 |
- set_server = function(f) {+ |
||
305 | -2x | +124 | +130x |
- stopifnot(inherits(f, "function"))+ if (length(private$args) == 0) { |
306 | -2x | +125 | +80x |
- stopifnot(all(c("id", "connection", "connectors") %in% names(formals(f))))+ private$args <- list()+ |
+
126 | ++ |
+ } |
||
307 | -2x | +127 | +130x |
- private$server <- f+ private$args[[name]] <- value+ |
+
128 | ++ | + | ||
308 | -2x | +129 | +130x |
- logger::log_trace("TealDataConnector$set_server server set.")+ private$refresh() |
309 | -2x | +130 | +130x |
- return(invisible(NULL))+ logger::log_trace("CallableFunction$set_arg_value args values set for arg: { deparse1(name) }.") |
310 | +131 |
- },+ |
||
311 | -+ | |||
132 | +130x |
- #' @description+ return(invisible(self)) |
||
312 | +133 |
- #' Set connector pre-open server function+ } |
||
313 | +134 |
- #'+ ), |
||
314 | +135 |
- #' This function will be called before submit button will be hit.+ |
||
315 | +136 |
- #'+ ## __Private Fields ==== |
||
316 | +137 |
- #' @param f (`function`)\cr+ private = list( |
||
317 | +138 |
- #' A shiny module server function+ fun_name = character(0), |
||
318 | +139 |
- #'+ args = NULL, # named list with argument names and values |
||
319 | +140 |
- #' @return nothing+ ## __Private Methods ==== |
||
320 | +141 |
- set_preopen_server = function(f) {+ # @description |
||
321 | -! | +|||
142 | +
- stopifnot(inherits(f, "function"))+ # Refresh call with function name and saved arguments |
|||
322 | -! | +|||
143 | +
- stopifnot(all(c("id", "connection") %in% names(formals(f))))+ # |
|||
323 | -! | +|||
144 | +
- private$preopen_server <- f+ # @return nothing |
|||
324 | -! | +|||
145 | +
- logger::log_trace("TealDataConnector$set_preopen_server preopen_server set.")+ refresh = function() { |
|||
325 | -! | +|||
146 | +339x |
- return(invisible(NULL))+ if (!is.null(private$fun_name) || !identical(private$fun_name, character(0))) { |
||
326 | +147 |
- },+ # replaced str2lang found at: |
||
327 | +148 |
-
+ # https://rlang.r-lib.org/reference/call2.html+ |
+ ||
149 | +339x | +
+ private$call <- as.call(+ |
+ ||
150 | +339x | +
+ c(rlang::parse_expr(private$fun_name), private$args) |
||
328 | +151 |
- # ___ pull ====+ ) |
||
329 | +152 |
- #' @description+ |
||
330 | +153 |
- #' Load data from each `TealDatasetConnector`+ # exception for source(...)$value+ |
+ ||
154 | +339x | +
+ if (private$fun_name == "source") {+ |
+ ||
155 | +9x | +
+ private$call <- rlang::parse_expr(+ |
+ ||
156 | +9x | +
+ sprintf("%s$value", deparse1(private$call, collapse = "\n")) |
||
331 | +157 |
- #'+ )+ |
+ ||
158 | +330x | +
+ } else if (private$fun_name %in% c("py_run_file", "py_run_string")) {+ |
+ ||
159 | +! | +
+ private$call <- rlang::parse_expr(+ |
+ ||
160 | +! | +
+ sprintf("%s[[%s]]", deparse1(private$call, collapse = "\n"), deparse1(private$object, collapse = "\n")) |
||
332 | +161 |
- #' @param con_args (`NULL` or named `list`)\cr+ ) |
||
333 | +162 |
- #' additional dynamic arguments for connection function. `args` will be passed to each+ } |
||
334 | +163 |
- #' `TealDatasetConnector` object to evaluate `CallableFunction` assigned to+ } |
||
335 | +164 |
- #' this dataset. If `args` is null than default set of arguments will be used, otherwise+ }, |
||
336 | +165 |
- #' call will be executed on these arguments only (arguments set before will be ignored).+ # @description |
||
337 | +166 |
- #' `pull` function doesn't update reproducible call, it's just evaluate function.+ # Returns a call to a function |
||
338 | +167 |
- #'+ # |
||
339 | +168 |
- #' @param args (`NULL` or named `list`)\cr+ # Returns the call to the function as defined in the enclosing environment. |
||
340 | +169 |
- #' additional dynamic arguments to pull dataset. `args` will be passed to each+ # |
||
341 | +170 |
- #' `TealDatasetConnector` object to evaluate `CallableFunction` assigned to+ # @param callable \code{function, character, call, symbol} the function to return |
||
342 | +171 |
- #' this dataset. If `args` is null than default set of arguments will be used, otherwise+ # |
||
343 | +172 |
- #' call will be executed on these arguments only (arguments set before will be ignored).+ # @return `call` the call to the function |
||
344 | +173 |
- #' `pull` function doesn't update reproducible call, it's just evaluate function.+ # |
||
345 | +174 |
- #'+ get_callable_function = function(callable) {+ |
+ ||
175 | +208x | +
+ if (is.character(callable) && private$is_prefixed_function(callable)) {+ |
+ ||
176 | +11x | +
+ private$get_call_from_prefixed_function(callable) |
||
346 | +177 |
- #' @param try (`logical` value)\cr+ } else {+ |
+ ||
178 | +197x | +
+ private$get_call_from_symbol(callable) |
||
347 | +179 |
- #' whether perform function evaluation inside `try` clause+ } |
||
348 | +180 |
- #'+ }, |
||
349 | +181 |
- #' @return (`self`) invisibly for chaining. In order to get the data please use `get_datasets` method.+ # @param function_name (`character`) the function name prefixed with \code{::} |
||
350 | +182 |
- pull = function(con_args = NULL, args = NULL, try = TRUE) {+ # and the package name |
||
351 | -3x | +|||
183 | +
- logger::log_trace("TealDataConnector$pull pulling data...")+ # @return `call` the call to the function passed to this method |
|||
352 | +184 |
- # open connection+ get_call_from_prefixed_function = function(function_name) { |
||
353 | -3x | +185 | +11x |
- if (!is.null(private$connection)) {+ package_function_names <- strsplit(function_name, "::")[[1]] |
354 | -3x | +186 | +11x |
- private$connection$open(args = con_args, try = try)+ fun <- get(package_function_names[2], envir = getNamespace(package_function_names[1])) |
355 | -+ | |||
187 | +11x |
-
+ if (!is.function(fun)) { |
||
356 | -3x | +188 | +1x |
- conn <- private$connection$get_conn()+ stop(sprintf("object '%s' of mode 'function' was not found", function_name)) |
357 | -3x | +|||
189 | +
- for (connector in private$datasets) {+ } |
|||
358 | -4x | +190 | +10x |
- connector$get_pull_callable()$assign_to_env("conn", conn)+ str2lang(function_name) |
359 | +191 |
- }+ }, |
||
360 | +192 |
- }+ # @param symbol (`function`, `symbol` or `character`) the item matching a function |
||
361 | +193 |
-
+ # @return `call` the call to the function passed to this method |
||
362 | +194 |
- # load datasets+ get_call_from_symbol = function(symbol) { |
||
363 | -3x | +195 | +197x |
- for (dataset in private$datasets) {+ fun <- match.fun(symbol) |
364 | -4x | +196 | +193x |
- load_dataset(dataset, args = args)+ fun_environment <- environment(fun) |
365 | -+ | |||
197 | +193x |
- }+ if (isNamespace(fun_environment)) { |
||
366 | -+ | |||
198 | +69x |
-
+ fun_name <- get_binding_name(fun, fun_environment) |
||
367 | -+ | |||
199 | +69x |
- # close connection+ namespace_name <- strsplit(rlang::env_name(fun_environment), ":")[[1]][2] |
||
368 | -3x | +200 | +69x |
- if (!is.null(private$connection)) private$connection$close(silent = TRUE)+ if (namespace_name != "base") {+ |
+
201 | +8x | +
+ fun_name <- paste(namespace_name, fun_name, sep = "::") |
||
369 | +202 |
-
+ } |
||
370 | -3x | +203 | +69x |
- logger::log_trace("TealDataConnector$pull data pulled.")+ fun <- str2lang(fun_name) |
371 | +204 |
-
+ } |
||
372 | -3x | +205 | +193x |
- return(invisible(self))+ fun |
373 | +206 |
}, |
||
374 | +207 |
- #' @description+ # Checks whether a character vector is of this format |
||
375 | +208 |
- #' Run simple application that uses its `ui` and `server` fields to pull data from+ # <package_name>::<function_name> |
||
376 | +209 |
- #' connection.+ # |
||
377 | +210 |
- #'+ # @param function_name (`character`) the character vector |
||
378 | +211 |
- #' Useful for debugging+ # @return `logical` `TRUE` if \code{function_name} is of the specified |
||
379 | +212 |
- #'+ # format; `FALSE` otherwise |
||
380 | +213 |
- #' @return An object that represents the app+ # |
||
381 | +214 |
- launch = function() {+ is_prefixed_function = function(function_name) { |
||
382 | -+ | |||
215 | +18x |
- # load TealDatasetConnector objects+ grepl("^[[:ascii:]]+::[[:ascii:]]+$", function_name, perl = TRUE) |
||
383 | -! | +|||
216 | +
- if (self$is_pulled()) {+ } |
|||
384 | -! | +|||
217 | +
- stop("All the datasets have already been pulled.")+ ) |
|||
385 | +218 |
- }+ ) |
||
386 | +219 | |||
387 | -! | +|||
220 | +
- shinyApp(+ ## Constructors ==== |
|||
388 | -! | +|||
221 | +
- ui = fluidPage(+ |
|||
389 | -! | +|||
222 | +
- include_js_files(),+ #' Create \code{CallableFunction} object |
|||
390 | -! | +|||
223 | +
- theme = get_teal_bs_theme(),+ #' |
|||
391 | -! | +|||
224 | +
- fluidRow(+ #' @description `r lifecycle::badge("stable")` |
|||
392 | -! | +|||
225 | +
- column(+ #' Create \code{\link{CallableFunction}} object to execute specific function and get reproducible |
|||
393 | -! | +|||
226 | +
- width = 8,+ #' call. |
|||
394 | -! | +|||
227 | +
- offset = 2,+ #' |
|||
395 | -! | +|||
228 | +
- tags$div(+ #' @param fun (\code{function})\cr |
|||
396 | -! | +|||
229 | +
- id = "data_inputs",+ #' any R function, directly by name or \code{character} string. |
|||
397 | -! | +|||
230 | +
- self$get_ui(id = "data_connector"),+ #' |
|||
398 | -! | +|||
231 | +
- actionButton("submit", "Submit"),+ #' @return \code{CallableFunction} object |
|||
399 | -! | +|||
232 | +
- `data-proxy-click` = "submit" # handled by jscode in custom.js - hit enter to submit+ #' |
|||
400 | +233 |
- ),+ #' @export |
||
401 | -! | +|||
234 | +
- shinyjs::hidden(+ #' |
|||
402 | -! | +|||
235 | +
- tags$div(+ #' @examples |
|||
403 | -! | +|||
236 | +
- id = "data_loaded",+ #' cf <- callable_function(fun = stats::median) |
|||
404 | -! | +|||
237 | +
- div(+ #' cf$set_args(list(x = 1:10, na.rm = FALSE)) |
|||
405 | -! | +|||
238 | +
- h3("Data successfully loaded."),+ #' cf$run() |
|||
406 | -! | +|||
239 | +
- p("You can close this window and get back to R console.")+ #' cf$get_call() |
|||
407 | +240 |
- )+ callable_function <- function(fun) {+ |
+ ||
241 | +161x | +
+ CallableFunction$new(fun) |
||
408 | +242 |
- )+ } |
||
409 | +243 |
- )+ |
||
410 | +244 |
- )+ #' Gets the name of the binding |
||
411 | +245 |
- )+ #' |
||
412 | +246 |
- ),+ #' Gets the name of the object by finding its origin. |
||
413 | -! | +|||
247 | +
- server = function(input, output, session) {+ #' Depending on type of object function uses different methods |
|||
414 | -! | +|||
248 | +
- session$onSessionEnded(stopApp)+ #' to obtain original location. If no `env` is specified then |
|||
415 | -! | +|||
249 | +
- self$get_preopen_server()(+ #' object is tracked by `substitute` along the `sys.frames`. |
|||
416 | -! | +|||
250 | +
- id = "data_connector",+ #' If `env` is specified then search is limited to specified |
|||
417 | -! | +|||
251 | +
- connection = private$connection+ #' environment.\cr |
|||
418 | +252 |
- )+ #' |
||
419 | -! | +|||
253 | +
- observeEvent(input$submit, {+ #' @note |
|||
420 | -! | +|||
254 | +
- rv <- reactiveVal(NULL)+ #' Raises an error if the object is not found in the environment. |
|||
421 | -! | +|||
255 | +
- rv(+ #' |
|||
422 | -! | +|||
256 | +
- self$get_server()(+ #' @param object (R object)\cr |
|||
423 | -! | +|||
257 | +
- id = "data_connector",+ #' any R object |
|||
424 | -! | +|||
258 | +
- connection = private$connection,+ #' @param envir (`environment`)\cr |
|||
425 | -! | +|||
259 | +
- connectors = private$datasets+ #' if origin of the object is known then should be provided for |
|||
426 | +260 |
- )+ #' more precise search |
||
427 | +261 |
- )+ #' @return character |
||
428 | +262 |
-
+ #' @keywords internal |
||
429 | -! | +|||
263 | +
- observeEvent(rv(), {+ #' |
|||
430 | -! | +|||
264 | +
- if (self$is_pulled()) {+ get_binding_name <- function(object, envir) { |
|||
431 | -! | +|||
265 | +70x |
- removeUI(sprintf("#%s", session$ns("data_inputs")))+ bindings_names <- ls(envir) |
||
432 | -! | +|||
266 | +70x |
- shinyjs::show("data_loaded")+ identical_binding_mask <- vapply( |
||
433 | -! | +|||
267 | +70x |
- stopApp()+ bindings_names, |
||
434 | -+ | |||
268 | +70x |
- }+ function(binding_name) identical(get(binding_name, envir), object), |
||
435 | -+ | |||
269 | +70x |
- })+ FUN.VALUE = logical(1), |
||
436 | -+ | |||
270 | +70x |
- })+ USE.NAMES = FALSE |
||
437 | +271 |
- }+ ) |
||
438 | -+ | |||
272 | +70x |
- )+ if (length(bindings_names[identical_binding_mask]) == 0) { |
||
439 | -+ | |||
273 | +1x |
- },+ stop("Object not found in the environment") |
||
440 | +274 |
-
+ }+ |
+ ||
275 | +69x | +
+ bindings_names[identical_binding_mask] |
||
441 | +276 |
- # ___ mutate ====+ } |
442 | +1 |
- #' @description+ ## JoinKeys ==== |
||
443 | +2 |
- #' Mutate data by code.+ #' |
||
444 | +3 |
- #'+ #' |
||
445 | +4 |
- #' @param ... parameters inherited from `TealDataAbstract`.+ #' @title R6 Class to store relationships for joining datasets |
||
446 | +5 |
- #'+ #' |
||
447 | +6 |
- #' @return Informational message to not use mutate_data() with `TealDataConnectors`.+ #' @description `r lifecycle::badge("stable")` |
||
448 | +7 |
- mutate = function(...) {+ #' This class stores symmetric links between pairs of key-values |
||
449 | -! | +|||
8 | +
- stop("TealDataConnectors do not support mutate_data().+ #' (e.g. column A of dataset X can be joined with column B of dataset Y). This relationship |
|||
450 | -! | +|||
9 | +
- Please use mutate_data() with teal_data() or cdisc_data()")+ #' is more general than the SQL foreign key relationship which also imposes constraints on the values |
|||
451 | +10 |
- },+ #' of these columns. |
||
452 | +11 |
-
+ #' @param dataset_1 (`character`) one dataset name |
||
453 | +12 |
- # ___ status ====+ #' @param dataset_2 (`character`) other dataset name |
||
454 | +13 |
- #' @description+ #' |
||
455 | +14 |
- #' Check if pull or connection has not failed.+ #' @examples |
||
456 | +15 |
- #'+ #' x <- teal.data:::JoinKeys$new() |
||
457 | +16 |
- #' @return `TRUE` if pull or connection failed, else `FALSE`+ #' x$set( |
||
458 | +17 |
- is_failed = function() {+ #' list( |
||
459 | -! | +|||
18 | +
- private$connection$is_failed() ||+ #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")), |
|||
460 | -! | +|||
19 | +
- any(vapply(private$datasets, function(x) x$is_failed(), logical(1)))+ #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y")) |
|||
461 | +20 |
- }+ #' ) |
||
462 | +21 |
- ),+ #' ) |
||
463 | +22 |
- ## __Private Fields ====+ #' x$get() |
||
464 | +23 |
- private = list(+ #' x$mutate("dataset_A", "dataset_B", c("col1" = "col10")) |
||
465 | +24 |
- server = NULL, # shiny server function+ #' x$get("dataset_A", "dataset_B") |
||
466 | +25 |
- preopen_server = NULL, # shiny server function+ JoinKeys <- R6::R6Class( # nolint |
||
467 | +26 |
- ui = NULL, # shiny ui function+ classname = "JoinKeys", |
||
468 | +27 |
- connection = NULL, # TealDataConnection+ ## __Public Methods ==== |
||
469 | +28 |
-
+ public = list( |
||
470 | +29 |
- ## __Private Methods ====+ #' @description |
||
471 | +30 |
- # adds open/close connection code at beginning/end of the dataset code+ #' Create a new object of `JoinKeys` |
||
472 | +31 |
- append_connection_code = function() {+ #' @return empty (`JoinKeys`) |
||
473 | -! | +|||
32 | +
- lapply(+ initialize = function() { |
|||
474 | -! | +|||
33 | +308x |
- private$datasets,+ logger::log_trace("JoinKeys initialized.") |
||
475 | -! | +|||
34 | +308x |
- function(connector) {+ return(invisible(self)) |
||
476 | -! | +|||
35 | +
- dataset <- get_dataset(connector)+ }, |
|||
477 | -! | +|||
36 | +
- try(+ #' @description |
|||
478 | -! | +|||
37 | +
- dataset$set_code(code = paste(+ #' Split the current `JoinKeys` object into a named list of join keys objects with an element for each dataset |
|||
479 | -! | +|||
38 | +
- c(+ #' @return (`list`) a list of `JoinKeys` object |
|||
480 | -! | +|||
39 | +
- if (!is.null(private$connection)) private$connection$get_open_call(deparse = TRUE),+ split = function() { |
|||
481 | -! | +|||
40 | +6x |
- get_code(dataset, deparse = TRUE, FUN.VALUE = character(1)),+ list_of_list_of_join_key_set <- lapply( |
||
482 | -! | +|||
41 | +6x |
- if (!is.null(private$connection)) private$connection$get_close_call(deparse = TRUE, silent = TRUE)+ names(self$get()), |
||
483 | -+ | |||
42 | +6x |
- ),+ function(dataset_1) { |
||
484 | -! | +|||
43 | +27x |
- collapse = "\n"+ lapply( |
||
485 | -+ | |||
44 | +27x |
- ))+ names(self$get()[[dataset_1]]), |
||
486 | +||||
45 | +27x | +
+ function(dataset_2) join_key(dataset_1, dataset_2, self$get()[[dataset_1]][[dataset_2]])+ |
+ ||
46 |
) |
|||
487 | +47 |
} |
||
488 | +48 |
) |
||
489 | -+ | |||
49 | +6x |
- }+ res <- lapply( |
||
490 | -+ | |||
50 | +6x |
- )+ list_of_list_of_join_key_set, |
||
491 | -+ | |||
51 | +6x |
- )+ function(x) { |
||
492 | -+ | |||
52 | +27x |
-
+ y <- JoinKeys$new() |
||
493 | -+ | |||
53 | +27x |
- #' The constructor for `TealDataConnector` class.+ y$set(x) |
||
494 | +54 |
- #'+ } |
||
495 | +55 |
- #' @description `r lifecycle::badge("stable")`+ ) |
||
496 | -+ | |||
56 | +6x |
- #' @param connection (`TealDataConnection`)\cr+ names(res) <- names(self$get()) |
||
497 | +57 |
- #' connection to data source+ |
||
498 | -+ | |||
58 | +6x |
- #' @param connectors (`list` of `TealDatasetConnector` elements)\cr+ logger::log_trace("JoinKeys$split keys split.") |
||
499 | -+ | |||
59 | +6x |
- #' list with dataset connectors+ return(res) |
||
500 | +60 |
- #'+ }, |
||
501 | +61 |
- #' @examples+ #' @description |
||
502 | +62 |
- #' library(magrittr)+ #' Merging a list (or one) of `JoinKeys` objects into the current `JoinKeys` object |
||
503 | +63 |
- #' random_data_connector <- function(dataname) {+ #' @param x `list` of `JoinKeys` objects or single `JoinKeys` object |
||
504 | +64 |
- #' fun_dataset_connector(+ #' @return (`self`) invisibly for chaining |
||
505 | +65 |
- #' dataname = dataname,+ merge = function(x) { |
||
506 | -+ | |||
66 | +5x |
- #' fun = teal.data::example_cdisc_data,+ if (inherits(x, "JoinKeys")) x <- list(x) |
||
507 | -+ | |||
67 | +18x |
- #' fun_args = list(dataname = dataname),+ checkmate::assert_list(x, types = "JoinKeys", min.len = 1) |
||
508 | -+ | |||
68 | +13x |
- #' )+ for (jk in x) { |
||
509 | -+ | |||
69 | +25x |
- #' }+ for (dataset_1 in names(jk$get())) { |
||
510 | -+ | |||
70 | +87x |
- #'+ for (dataset_2 in names(jk$get()[[dataset_1]])) { |
||
511 | -+ | |||
71 | +102x |
- #' open_fun <- callable_function(library)+ self$mutate(dataset_1, dataset_2, jk$get()[[dataset_1]][[dataset_2]]) |
||
512 | +72 |
- #' open_fun$set_args(list(package = "teal.data"))+ } |
||
513 | +73 |
- #'+ } |
||
514 | +74 |
- #' con <- data_connection(open_fun = open_fun)+ } |
||
515 | -+ | |||
75 | +13x |
- #' con$set_open_server(+ logger::log_trace("JoinKeys$merge keys merged.") |
||
516 | -+ | |||
76 | +13x |
- #' function(id, connection) {+ return(invisible(self)) |
||
517 | +77 |
- #' moduleServer(+ }, |
||
518 | +78 |
- #' id = id,+ #' @description |
||
519 | +79 |
- #' module = function(input, output, session) {+ #' Get join keys between two datasets. |
||
520 | +80 |
- #' connection$open(try = TRUE)+ #' @return (`character`) named character vector x with names(x) the |
||
521 | +81 |
- #' return(invisible(connection))+ #' columns of `dataset_1` and the values of `(x)` the corresponding join |
||
522 | +82 |
- #' }+ #' keys in `dataset_2` or `character(0)` if no relationship |
||
523 | +83 |
- #' )+ #' @details if one or both of `dataset_1` and `dataset_2` are missing then |
||
524 | +84 |
- #' }+ #' underlying keys structure is returned for further processing |
||
525 | +85 |
- #' )+ get = function(dataset_1, dataset_2) { |
||
526 | -+ | |||
86 | +994x |
- #'+ if (missing(dataset_1) && missing(dataset_2)) { |
||
527 | -+ | |||
87 | +375x |
- #' x <- relational_data_connector(+ return(private$.keys) |
||
528 | +88 |
- #' connection = con,+ } |
||
529 | -+ | |||
89 | +619x |
- #' connectors = list(+ if (missing(dataset_2)) { |
||
530 | -+ | |||
90 | +95x |
- #' random_data_connector(dataname = "ADSL"),+ return(private$.keys[[dataset_1]]) |
||
531 | +91 |
- #' random_data_connector(dataname = "ADLB")+ } |
||
532 | -+ | |||
92 | +524x |
- #' )+ if (missing(dataset_1)) { |
||
533 | -+ | |||
93 | +1x |
- #' )+ return(private$.keys[[dataset_2]]) |
||
534 | +94 |
- #'+ } |
||
535 | -+ | |||
95 | +523x |
- #' x$set_ui(+ if (is.null(private$.keys[[dataset_1]][[dataset_2]])) { |
||
536 | -+ | |||
96 | +153x |
- #' function(id, connection, connectors) {+ return(character(0)) |
||
537 | +97 |
- #' ns <- NS(id)+ } |
||
538 | -+ | |||
98 | +370x |
- #' tagList(+ return(private$.keys[[dataset_1]][[dataset_2]]) |
||
539 | +99 |
- #' connection$get_open_ui(ns("open_connection")),+ }, |
||
540 | +100 |
- #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1),+ #' @description |
||
541 | +101 |
- #' do.call(+ #' Change join_keys for a given pair of dataset names (or |
||
542 | +102 |
- #' what = "tagList",+ #' add join_keys for given pair if it does not exist) |
||
543 | +103 |
- #' args = lapply(+ #' @param val (named `character`) column names used to join |
||
544 | +104 |
- #' connectors,+ #' @return (`self`) invisibly for chaining |
||
545 | +105 |
- #' function(connector) {+ mutate = function(dataset_1, dataset_2, val) { |
||
546 | -+ | |||
106 | +236x |
- #' div(+ checkmate::assert_string(dataset_1) |
||
547 | -+ | |||
107 | +236x |
- #' connector$get_ui(+ checkmate::assert_string(dataset_2) |
||
548 | -+ | |||
108 | +236x |
- #' id = ns(connector$get_dataname())+ checkmate::assert_character(val, any.missing = FALSE) |
||
549 | +109 |
- #' ),+ |
||
550 | -+ | |||
110 | +236x |
- #' br()+ private$join_pair(join_key(dataset_1, dataset_2, val)) |
||
551 | +111 |
- #' )+ |
||
552 | -+ | |||
112 | +236x |
- #' }+ logger::log_trace( |
||
553 | -+ | |||
113 | +236x |
- #' )+ sprintf( |
||
554 | -+ | |||
114 | +236x |
- #' )+ "JoinKeys$mutate updated the keys between %s and %s to %s", |
||
555 | -+ | |||
115 | +236x |
- #' )+ dataset_1, |
||
556 | -+ | |||
116 | +236x |
- #' }+ dataset_2, |
||
557 | -+ | |||
117 | +236x |
- #' )+ paste(val, collapse = ", ") |
||
558 | +118 |
- #'+ ) |
||
559 | +119 |
- #' x$set_server(+ ) |
||
560 | -+ | |||
120 | +236x |
- #' function(id, connection, connectors) {+ return(invisible(self)) |
||
561 | +121 |
- #' moduleServer(+ }, |
||
562 | +122 |
- #' id = id,+ #' @description |
||
563 | +123 |
- #' module = function(input, output, session) {+ #' Set up join keys basing on list of `JoinKeySet` objects. |
||
564 | +124 |
- #' # opens connection+ #' @param x `list` of `JoinKeySet` objects (which are created using the `join_key` function) |
||
565 | +125 |
- #' connection$get_open_server()(id = "open_connection", connection = connection)+ #' or single `JoinKeySet` objects |
||
566 | +126 |
- #' if (connection$is_opened()) {+ #' @details Note that join keys are symmetric although the relationship only needs |
||
567 | +127 |
- #' for (connector in connectors) {+ #' to be specified once |
||
568 | +128 |
- #' set_args(connector, args = list(n = input$n))+ #' @return (`self`) invisibly for chaining |
||
569 | +129 |
- #' # pull each dataset+ set = function(x) { |
||
570 | -+ | |||
130 | +169x |
- #' connector$get_server()(id = connector$get_dataname())+ if (length(private$.keys) > 0) { |
||
571 | -+ | |||
131 | +1x |
- #' if (connector$is_failed()) {+ stop("Keys already set, please use JoinKeys$mutate() to change them") |
||
572 | +132 |
- #' break+ } |
||
573 | -+ | |||
133 | +168x |
- #' }+ if (inherits(x, "JoinKeySet")) { |
||
574 | -- |
- #' }- |
- ||
575 | -+ | |||
134 | +! |
- #' }+ x <- list(x) |
||
576 | +135 |
- #' }+ } |
||
577 | +136 |
- #' )+ |
||
578 | +137 |
- #' }+ # check if any JoinKeySets share the same datasets but different values |
||
579 | -+ | |||
138 | +168x |
- #' )+ for (idx_1 in seq_along(x)) { |
||
580 | -+ | |||
139 | +306x |
- #' \dontrun{+ for (idx_2 in seq_len(idx_1)) { |
||
581 | -+ | |||
140 | +502x |
- #' x$launch()+ private$check_compatible_keys(x[[idx_1]], x[[idx_2]]) |
||
582 | +141 |
- #' x$get_datasets()+ } |
||
583 | +142 |
- #' }+ } |
||
584 | +143 |
- #'+ |
||
585 | -+ | |||
144 | +160x |
- #' @return `TealDataConnector` object+ checkmate::assert_list(x, types = "JoinKeySet", min.len = 1) |
||
586 | -+ | |||
145 | +160x |
- #' @export+ lapply(x, private$join_pair) |
||
587 | +146 |
- relational_data_connector <- function(connection, connectors) {- |
- ||
588 | -2x | -
- stopifnot(inherits(connection, "TealDataConnection"))+ |
||
589 | -2x | +147 | +160x |
- checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1)+ logger::log_trace("JoinKeys$set keys are set.") |
590 | -2x | +148 | +160x |
- TealDataConnector$new(connection, connectors)+ return(invisible(self)) |
591 | +149 |
- }+ }, |
1 | +150 |
- ## TealData ====+ #' @description |
||
2 | +151 |
- #' @title Manage multiple `TealDataConnector`, `TealDatasetConnector` and `TealDataset` objects.+ #' Prints this `JoinKeys`. |
||
3 | +152 |
- #'+ #' |
||
4 | +153 |
- #' @description `r lifecycle::badge("experimental")`+ #' @param ... additional arguments to the printing method |
||
5 | +154 |
- #' Class manages `TealDataConnector`, `TealDatasetConnector` and+ #' @return invisibly self |
||
6 | +155 |
- #' `TealDataset` objects and aggregate them in one collection.+ print = function(...) { |
||
7 | -+ | |||
156 | +2x |
- #' Class also decides whether to launch app before initialize teal application.+ check_ellipsis(...) |
||
8 | -+ | |||
157 | +2x |
- #'+ keys_list <- self$get() |
||
9 | -+ | |||
158 | +2x |
- #' @param ... (`TealDataConnector`, `TealDataset`, `TealDatasetConnector`)\cr+ if (length(keys_list) > 0) { |
||
10 | -+ | |||
159 | +1x |
- #' objects+ cat(sprintf( |
||
11 | -+ | |||
160 | +1x |
- #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr+ "A JoinKeys object containing foreign keys between %s datasets:\n", |
||
12 | -+ | |||
161 | +1x |
- #' (optional) object with dataset column relationships used for joining.+ length(keys_list) |
||
13 | +162 |
- #' If empty then an empty `JoinKeys` object is passed by default.+ )) |
||
14 | -+ | |||
163 | +1x |
- #' @param check (`logical`) reproducibility check - whether evaluated preprocessing code gives the same objects+ print(keys_list) |
||
15 | +164 |
- #' as provided in arguments. Check is run only if flag is true and preprocessing code is not empty.+ } else { |
||
16 | -+ | |||
165 | +1x |
- #'+ cat("An empty JoinKeys object.") |
||
17 | +166 |
- #' @examples+ } |
||
18 | -+ | |||
167 | +2x |
- #' adsl_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADSL"))+ invisible(self) |
||
19 | +168 |
- #' adlb_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADLB"))+ }, |
||
20 | +169 |
- #' adrs_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADRS"))+ #' @description |
||
21 | +170 |
- #' adtte_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADTTE"))+ #' Sets the parents of the datasets. |
||
22 | +171 |
- #' x1 <- cdisc_dataset_connector("ADSL", adsl_cf, keys = get_cdisc_keys("ADSL"))+ #' |
||
23 | +172 |
- #' x2 <- cdisc_dataset_connector("ADRS", adrs_cf, keys = get_cdisc_keys("ADRS"))+ #' @param named_list Named (`list`) of the parents datasets. |
||
24 | +173 |
- #' x3 <- cdisc_dataset(+ #' |
||
25 | +174 |
- #' dataname = "ADAE",+ #' @return (`self`) invisibly for chaining |
||
26 | +175 |
- #' x = teal.data::example_cdisc_data("ADAE"),+ set_parents = function(named_list) { |
||
27 | -+ | |||
176 | +34x |
- #' code = "library(teal.data)\nADAE <- teal.data::example_cdisc_data(\"ADAE\")"+ for (dataset in names(named_list)) { |
||
28 | -+ | |||
177 | +74x |
- #' )+ checkmate::assert( |
||
29 | -+ | |||
178 | +74x |
- #' x4 <- cdisc_dataset_connector("ADTTE", adtte_cf, keys = get_cdisc_keys("ADTTE"))+ checkmate::check_null(self$get_parent(dataset)), |
||
30 | -+ | |||
179 | +74x |
- #' tc <- teal.data:::TealData$new(x1, x2, x3, x4)+ checkmate::check_true( |
||
31 | -+ | |||
180 | +74x |
- #' tc$get_datanames()+ length(self$get_parent(dataset)) == 0 && |
||
32 | -+ | |||
181 | +74x |
- #' \dontrun{+ length(named_list[[dataset]]) == 0 |
||
33 | +182 |
- #' tc$launch()+ ), |
||
34 | -+ | |||
183 | +74x |
- #' get_datasets(tc) # equivalent to tc$get_datasets()+ checkmate::check_true(self$get_parent(dataset) == named_list[[dataset]]), |
||
35 | -+ | |||
184 | +74x |
- #' tc$get_dataset("ADAE")+ "Please check the difference between provided datasets parents and provided join_keys parents." |
||
36 | +185 |
- #' tc$check()+ ) |
||
37 | -+ | |||
186 | +73x |
- #' }+ if (is.null(self$get_parent(dataset))) { |
||
38 | -+ | |||
187 | +70x |
- #'+ private$parents[[dataset]] <- named_list[[dataset]] |
||
39 | +188 |
- #' x <- cdisc_dataset(+ } |
||
40 | +189 |
- #' dataname = "ADSL",+ } |
||
41 | -+ | |||
190 | +33x |
- #' x = teal.data::example_cdisc_data("ADSL"),+ invisible(self) |
||
42 | +191 |
- #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ }, |
||
43 | +192 |
- #' )+ #' @description |
||
44 | +193 |
- #'+ #' Gets the parent of the desired dataset. |
||
45 | +194 |
- #' x2 <- cdisc_dataset_connector("ADTTE", adtte_cf, keys = get_cdisc_keys("ADTTE"))+ #' |
||
46 | +195 |
- #' tc <- teal.data:::TealData$new(x, x2)+ #' @param dataname (`character`) name of the dataset. |
||
47 | +196 |
- #' \dontrun{+ #' @return (`character`) the parent of the desired dataset |
||
48 | +197 |
- #' # This errors as we have not pulled the data+ get_parent = function(dataname) { |
||
49 | -+ | |||
198 | +241x |
- #' # tc$get_datasets()+ if (missing(dataname)) { |
||
50 | -+ | |||
199 | +1x |
- #' # pull the data and then we can get the datasets+ return(NULL) |
||
51 | +200 |
- #' tc$launch()+ } |
||
52 | -+ | |||
201 | +240x |
- #' tc$get_datasets()+ private$parents[[dataname]] |
||
53 | +202 |
- #' get_raw_data(tc)+ }, |
||
54 | +203 |
- #' }+ #' @description |
||
55 | +204 |
- #'+ #' Gets the parents of the datasets. |
||
56 | +205 |
- TealData <- R6::R6Class( # nolint+ #' |
||
57 | +206 |
- classname = "TealData",+ #' @return (`list`) A named list of the parents of all datasets |
||
58 | +207 |
- inherit = TealDataAbstract,+ get_parents = function() { |
||
59 | -+ | |||
208 | +53x |
- ## __Public Methods ====+ private$parents |
||
60 | +209 |
- public = list(+ }, |
||
61 | +210 |
#' @description |
||
62 | +211 |
- #' Create a new object of `TealData` class+ #' Updates the keys of the datasets based on the parents. |
||
63 | +212 |
- initialize = function(..., check = FALSE, join_keys = teal.data::join_keys()) {+ #' |
||
64 | -133x | +|||
213 | +
- checkmate::assert_class(join_keys, "JoinKeys")+ #' @return (`self`) invisibly for chaining |
|||
65 | +214 |
-
+ update_keys_given_parents = function() { |
||
66 | -133x | +215 | +22x |
- dot_args <- list(...)+ datanames <- names(self$get()) |
67 | -133x | +216 | +22x |
- is_teal_data <- checkmate::test_list(+ duplicate_pairs <- list() |
68 | -133x | +217 | +22x |
- dot_args,+ for (d1 in datanames) { |
69 | -133x | +218 | +46x |
- types = c("TealDataConnector", "TealDataset", "TealDatasetConnector")+ d1_pk <- self$get(d1, d1) |
70 | -+ | |||
219 | +46x |
- )+ d1_parent <- self$get_parent(d1) |
||
71 | -133x | +220 | +46x |
- if (!all(is_teal_data)) {+ for (d2 in datanames) { |
72 | -2x | +221 | +112x |
- stop("All elements should be of TealDataset(Connector) or TealDataConnector class")+ if (paste(d2, d1) %in% duplicate_pairs) { |
73 | -+ | |||
222 | +26x |
- }+ next |
||
74 | +223 |
-
+ } |
||
75 | -131x | +224 | +86x |
- datanames <- unlist(lapply(dot_args, get_dataname))+ if (length(self$get(d1, d2)) == 0) { |
76 | -131x | -
- private$check_names(datanames)- |
- ||
77 | -+ | 225 | +38x |
-
+ d2_parent <- self$get_parent(d2) |
78 | -130x | +226 | +38x |
- private$datasets <- dot_args+ d2_pk <- self$get(d2, d2) |
79 | +227 | |||
80 | -130x | +228 | +38x |
- self$set_check(check)+ fk <- if (identical(d1, d2_parent)) { |
81 | +229 |
-
+ # first is parent of second -> parent keys -> first keys |
||
82 | -130x | +230 | +18x |
- private$pull_code <- CodeClass$new()+ d1_pk |
83 | -130x | +231 | +38x |
- private$mutate_code <- CodeClass$new()+ } else if (identical(d1_parent, d2)) { |
84 | +232 | - - | -||
85 | -130x | -
- private$join_keys <- join_keys+ # second is parent of first -> parent keys -> second keys |
||
86 | -+ | |||
233 | +! |
-
+ d2_pk |
||
87 | -130x | +234 | +38x |
- self$id <- sample.int(1e11, 1, useHash = TRUE)+ } else if (identical(d1_parent, d2_parent) && length(d1_parent) > 0) { |
88 | +235 |
-
+ # both has the same parent -> parent keys |
||
89 | -130x | +236 | +10x |
- logger::log_trace(+ self$get(d1_parent, d1_parent) |
90 | -130x | +|||
237 | +
- "TealData initialized with data: { paste(self$get_datanames(), collapse = ' ') }."+ } else { |
|||
91 | +238 |
- )+ # cant find connection - leave empty |
||
92 | -130x | -
- return(invisible(self))- |
- ||
93 | -+ | 239 | +10x |
- },+ next |
94 | +240 |
- #' @description+ } |
||
95 | -+ | |||
241 | +28x |
- #' Creates a copy of the object with keeping valid references+ self$mutate(d1, d2, fk) |
||
96 | -+ | |||
242 | +28x |
- #' between `TealDataset` and `TealDatasetConnector` objects+ duplicate_pairs <- append(duplicate_pairs, paste(d1, d2)) |
||
97 | +243 |
- #' @param deep (`logical(1)`)\cr+ } |
||
98 | +244 |
- #' argument passed to `clone` method. If `TRUE` deep copy is made+ } |
||
99 | +245 |
- #' @return self invisible+ } |
||
100 | +246 |
- copy = function(deep = FALSE) {- |
- ||
101 | -2x | -
- new_self <- self$clone(deep = deep)+ # check parent child relation |
||
102 | -2x | +247 | +22x |
- new_self$reassign_datasets_vars()+ private$check_parent_child() |
103 | -2x | +|||
248 | +
- logger::log_trace("TealData$copy{if (deep) ' deep-' else ' '}copied self.")+ |
|||
104 | -2x | +249 | +22x |
- invisible(new_self)+ invisible(self) |
105 | +250 |
- },+ } |
||
106 | +251 |
- #' @description+ ), |
||
107 | +252 |
- #' Prints this `TealData`.+ ## __Private Fields ==== |
||
108 | +253 |
- #'+ private = list( |
||
109 | +254 |
- #' @param ... additional arguments to the printing method+ .keys = list(), |
||
110 | +255 |
- #' @return invisibly self+ parents = list(), |
||
111 | +256 |
- print = function(...) {+ join_pair = function(join_key) { |
||
112 | -1x | +257 | +527x |
- check_ellipsis(...)+ dataset_1 <- join_key$dataset_1 |
113 | -+ | |||
258 | +527x |
-
+ dataset_2 <- join_key$dataset_2 |
||
114 | -1x | +259 | +527x |
- cat(sprintf(+ keys <- join_key$keys |
115 | -1x | +|||
260 | +
- "A %s object containing %d TealDataset/TealDatasetConnector object(s) as element(s):\n",+ |
|||
116 | -1x | +261 | +527x |
- class(self)[1],+ if (is.null(private$.keys[[dataset_1]])) { |
117 | -1x | +262 | +283x |
- length(private$datasets)+ private$.keys[[dataset_1]] <- list() |
118 | +263 |
- ))+ }+ |
+ ||
264 | +527x | +
+ private$.keys[[dataset_1]][[dataset_2]] <- keys |
||
119 | +265 | |||
120 | -1x | +266 | +527x |
- for (i in seq_along(private$datasets)) {+ if (dataset_2 != dataset_1) { |
121 | -2x | +267 | +348x |
- cat(sprintf("--> Element %d:\n", i))+ if (is.null(private$.keys[[dataset_2]])) { |
122 | -2x | +268 | +199x |
- print(private$datasets[[i]])+ private$.keys[[dataset_2]] <- list() |
123 | +269 |
- }+ } |
||
124 | +270 | |||
125 | -1x | +271 | +348x |
- invisible(self)+ if (length(keys) > 0) { |
126 | -+ | |||
272 | +343x |
- },+ keys <- setNames(names(keys), keys) |
||
127 | +273 |
- # ___ getters ====+ }+ |
+ ||
274 | +348x | +
+ private$.keys[[dataset_2]][[dataset_1]] <- keys |
||
128 | +275 |
- #' @description+ } |
||
129 | +276 |
- #' Get data connectors.+ }, |
||
130 | +277 |
- #'+ # helper function to deterimine if two key sets contain incompatible keys |
||
131 | +278 |
- #' @return (`list`) with all `TealDatasetConnector` or `TealDataConnector` objects.+ # return TRUE if compatible, throw error otherwise |
||
132 | +279 |
- get_connectors = function() {+ check_compatible_keys = function(join_key_1, join_key_2) { |
||
133 | -5x | +280 | +502x |
- return(Filter(+ error_message <- function(dataset_1, dataset_2) { |
134 | -5x | +281 | +7x |
- function(x) {+ stop( |
135 | -9x | +282 | +7x |
- inherits(x, "TealDatasetConnector") || inherits(x, "TealDataConnector")+ paste("cannot specify multiple different join keys between datasets:", dataset_1, "and", dataset_2) |
136 | +283 |
- },+ ) |
||
137 | -5x | +|||
284 | +
- private$datasets+ } |
|||
138 | +285 |
- ))+ |
||
139 | +286 |
- },+ |
||
140 | +287 |
- #' @description+ # if first datasets and the second datasets match and keys |
||
141 | +288 |
- #' Get all datasets and all dataset connectors+ # must contain the same named elements |
||
142 | -+ | |||
289 | +502x |
- #'+ if (join_key_1$dataset_1 == join_key_2$dataset_1 && join_key_1$dataset_2 == join_key_2$dataset_2) { |
||
143 | -+ | |||
290 | +305x |
- #' @param dataname (`character` value)\cr+ if (!identical(sort(join_key_1$keys), sort(join_key_2$keys))) { |
||
144 | -+ | |||
291 | +3x |
- #' name of dataset connector to be returned. If `NULL`, all connectors are returned.+ error_message(join_key_1$dataset_1, join_key_1$dataset_2) |
||
145 | +292 |
- #'+ } |
||
146 | +293 |
- #' @return `list` with all datasets and all connectors+ } |
||
147 | +294 |
- get_items = function(dataname = NULL) {+ |
||
148 | -105x | +|||
295 | +
- checkmate::assert_string(dataname, null.ok = TRUE)+ # if first dataset of join_key_1 matches second dataset of join_key_2 |
|||
149 | +296 |
-
+ # and the first dataset of join_key_2 must match second dataset of join_key_1 |
||
150 | -105x | +|||
297 | +
- get_sets <- function(x) {+ # and keys must contain the same elements but with names and values swapped |
|||
151 | -195x | +298 | +498x |
- if (inherits(x, "TealDataConnector")) {+ if (join_key_1$dataset_1 == join_key_2$dataset_2 && join_key_1$dataset_2 == join_key_2$dataset_1) { |
152 | -12x | +|||
299 | +
- x$get_items()+ # have to handle empty case differently as names(character(0)) is NULL |
|||
153 | -+ | |||
300 | +92x |
- } else {+ if (length(join_key_1$keys) == 0 && length(join_key_2$keys) == 0) { |
||
154 | -183x | +301 | +2x |
- x+ return(TRUE) |
155 | +302 |
} |
||
156 | +303 |
- }+ |
||
157 | +304 |
-
+ if ( |
||
158 | -105x | +305 | +90x |
- sets <- unlist(lapply(private$datasets, get_sets))+ xor(length(join_key_1$keys) == 0, length(join_key_2$keys) == 0) || |
159 | -105x | +306 | +90x |
- names(sets) <- vapply(sets, get_dataname, character(1))+ !identical(sort(join_key_1$keys), sort(setNames(names(join_key_2$keys), join_key_2$keys))) |
160 | +307 |
-
+ ) { |
||
161 | -104x | +308 | +4x |
- if (checkmate::test_string(dataname)) {+ error_message(join_key_1$dataset_1, join_key_1$dataset_2) |
162 | -5x | +|||
309 | +
- if (!(dataname %in% self$get_datanames())) {+ } |
|||
163 | -2x | +|||
310 | +
- stop(paste("dataset", dataname, "not found"))+ } |
|||
164 | +311 |
- }- |
- ||
165 | -3x | -
- return(sets[[dataname]])+ |
||
166 | +312 |
- } else {+ # otherwise they are compatible |
||
167 | -99x | +313 | +492x |
- return(sets)+ return(TRUE) |
168 | +314 |
- }+ }, |
||
169 | +315 |
- },+ # checks the parent child relations are valid |
||
170 | +316 |
-
+ check_parent_child = function() { |
||
171 | -+ | |||
317 | +24x |
- #' @description+ if (!is.null(self$get_parents())) { |
||
172 | -+ | |||
318 | +24x |
- #' Get join keys between two datasets.+ parents <- self$get_parents() |
||
173 | -+ | |||
319 | +24x |
- #'+ for (idx1 in seq_along(parents)) { |
||
174 | -+ | |||
320 | +46x |
- #' @param dataset_1 (`character`) name of first dataset.+ name_from <- names(parents)[[idx1]] |
||
175 | -+ | |||
321 | +46x |
- #' @param dataset_2 (`character`) name of second dataset.+ for (idx2 in seq_along(parents[[idx1]])) { |
||
176 | -+ | |||
322 | +21x |
- #' @return (`character`) named character vector x with names(x) the+ name_to <- parents[[idx1]][[idx2]] |
||
177 | -+ | |||
323 | +21x |
- #' columns of `dataset_1` and the values of `(x)` the corresponding join+ keys_from <- self$get(name_from, name_to) |
||
178 | -+ | |||
324 | +21x |
- #' keys in `dataset_2` or `character(0)` if no relationship+ keys_to <- self$get(name_to, name_from)+ |
+ ||
325 | +21x | +
+ if (length(keys_from) == 0 && length(keys_to) == 0) {+ |
+ ||
326 | +1x | +
+ stop(sprintf("No join keys from %s to its parent (%s) and vice versa", name_from, name_to)) |
||
179 | +327 |
- get_join_keys = function(dataset_1, dataset_2) {+ } |
||
180 | -179x | +328 | +20x |
- if (missing(dataset_1) && missing(dataset_2)) {+ if (length(keys_from) == 0) { |
181 | -16x | +|||
329 | +! |
- private$join_keys+ stop(sprintf("No join keys from %s to its parent (%s)", name_from, name_to)) |
||
182 | +330 |
- } else {+ } |
||
183 | -163x | +331 | +20x |
- private$join_keys$get(dataset_1, dataset_2)+ if (length(keys_to) == 0) { |
184 | -+ | |||
332 | +! |
- }+ stop(sprintf("No join keys from %s parent name (%s) to %s", name_from, name_to, name_from)) |
||
185 | +333 |
- },+ } |
||
186 | +334 |
-
+ } |
||
187 | +335 |
- #' @description+ } |
||
188 | +336 |
- #' returns the parents list of the datasets.+ } |
||
189 | +337 |
- #'+ } |
||
190 | +338 |
- #' @return named (`list`) of the parents of all datasets.+ ) |
||
191 | +339 |
- get_parents = function() {+ ) |
||
192 | -1x | +|||
340 | +
- private$join_keys$get_parents()+ |
|||
193 | +341 |
- },+ # constructors ==== |
||
194 | +342 | |||
195 | +343 |
- # ___ shiny ====+ #' Create a `JoinKeys` out of a list of `JoinKeySet` objects |
||
196 | +344 |
-
+ #' |
||
197 | +345 |
- #' @description+ #' @description `r lifecycle::badge("stable")` |
||
198 | +346 |
- #'+ #' |
||
199 | +347 |
- #' Get a shiny-module UI to render the necessary app to+ #' @param ... optional, a `JoinKeySet` objects created using the `join_key` function. |
||
200 | +348 |
- #' derive `TealDataConnector` object's data+ #' @details Note that join keys are symmetric although the relationship only needs |
||
201 | +349 |
- #'+ #' to be specified once. |
||
202 | +350 |
- #' @param id (`character`) item ID for the shiny module+ #' |
||
203 | +351 |
- #' @return the `shiny` `ui` function+ #' @return `JoinKeys` |
||
204 | +352 |
- get_ui = function(id) {+ #' |
||
205 | -4x | +|||
353 | +
- if (is.null(private$ui)) {+ #' @export |
|||
206 | -! | +|||
354 | +
- div(id = id, "Data Loaded")+ #' |
|||
207 | +355 |
- } else {+ #' @examples |
||
208 | -4x | +|||
356 | +
- private$ui(id)+ #' join_keys() |
|||
209 | +357 |
- }+ #' join_keys( |
||
210 | +358 |
- },+ #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")), |
||
211 | +359 |
- #' @description+ #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y")) |
||
212 | +360 |
- #'+ #' ) |
||
213 | +361 |
- #' Get a shiny-module server to render the necessary app to+ #' join_keys( |
||
214 | +362 |
- #' derive `TealDataConnector` object's data+ #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")) |
||
215 | +363 |
- #'+ #' ) |
||
216 | +364 |
- #' @return `shiny` server module.+ join_keys <- function(...) { |
||
217 | -+ | |||
365 | +242x |
- get_server = function() {+ x <- list(...) |
||
218 | -! | +|||
366 | +242x |
- if (is.null(private$server)) {+ res <- JoinKeys$new() |
||
219 | -! | +|||
367 | +242x |
- return(+ if (length(x) > 0) { |
||
220 | -! | +|||
368 | +109x |
- function(id) {+ res$set(x) |
||
221 | -! | +|||
369 | +
- moduleServer(+ } |
|||
222 | -! | +|||
370 | +
- id = id,+ |
|||
223 | -! | +|||
371 | +234x |
- module = function(input, output, session) {+ res |
||
224 | -! | +|||
372 | +
- reactive(self)+ } |
|||
225 | +373 |
- }+ |
||
226 | +374 |
- )+ #' @title Getter for JoinKeys that returns the relationship between pairs of datasets |
||
227 | +375 |
- }+ #' @param x JoinKeys object to extract the join keys |
||
228 | +376 |
- )+ #' @param dataset_1 (`character`) name of first dataset. |
||
229 | +377 |
- } else {+ #' @param dataset_2 (`character`) name of second dataset. |
||
230 | -! | +|||
378 | +
- function(id) {+ #' @export |
|||
231 | -! | +|||
379 | +
- moduleServer(+ `[.JoinKeys` <- function(x, dataset_1, dataset_2 = NULL) { |
|||
232 | -! | +|||
380 | +23x |
- id = id,+ checkmate::assert_string(dataset_1) |
||
233 | -! | +|||
381 | +23x |
- module = private$server+ checkmate::assert_string(dataset_2, null.ok = TRUE) |
||
234 | +382 |
- )+ |
||
235 | -+ | |||
383 | +23x |
- }+ dataset_2 <- dataset_2 %||% dataset_1 |
||
236 | -+ | |||
384 | +23x |
- }+ x$get(dataset_1, dataset_2) |
||
237 | +385 |
- },+ } |
||
238 | +386 |
- #' @description+ |
||
239 | +387 |
- #'+ #' @rdname sub-.JoinKeys |
||
240 | +388 |
- #' Launch an app that allows to run the user-interfaces of all+ #' @param value value to assign |
||
241 | +389 |
- #' `TealDataConnector` and `TealDatasetConnector` modules+ #' @export |
||
242 | +390 |
- #'+ `[<-.JoinKeys` <- function(x, dataset_1, dataset_2 = NULL, value) { |
||
243 | -+ | |||
391 | +4x |
- #' This piece is mainly used for debugging.+ checkmate::assert_string(dataset_1) |
||
244 | -+ | |||
392 | +4x |
- launch = function() {+ checkmate::assert_string(dataset_2, null.ok = TRUE) |
||
245 | +393 |
- # if no data connectors can append any dataset connectors+ |
||
246 | -+ | |||
394 | +4x |
- # and not load an app+ dataset_2 <- dataset_2 %||% dataset_1 |
||
247 | -! | +|||
395 | +4x |
- if (self$is_pulled()) {+ x$mutate(dataset_1, dataset_2, value) |
||
248 | -! | +|||
396 | +4x |
- stop("All the datasets have already been pulled.")+ x |
||
249 | +397 |
- }+ } |
||
250 | +398 | |||
251 | +399 |
- # otherwise load TealDataConnector and+ #' @rdname join_keys |
||
252 | +400 |
- # TealDatasetConnector with shiny app+ #' @details |
||
253 | -! | +|||
401 | +
- shinyApp(+ #' `cdisc_join_keys` treat non-`JoinKeySet` arguments as possible CDISC datasets. |
|||
254 | -! | +|||
402 | +
- ui = fluidPage(+ #' The `dataname` is extrapolated from the name (or fallback to the value itself if |
|||
255 | -! | +|||
403 | +
- theme = get_teal_bs_theme(),+ #' it's a `character(1)`). |
|||
256 | -! | +|||
404 | +
- fluidRow(+ #' |
|||
257 | -! | +|||
405 | +
- column(+ #' @export |
|||
258 | -! | +|||
406 | +
- width = 8,+ #' @examples |
|||
259 | -! | +|||
407 | +
- offset = 2,+ #' cdisc_join_keys(join_key("dataset_A", "dataset_B", c("col_1" = "col_a")), "ADTTE") |
|||
260 | -! | +|||
408 | +
- self$get_ui(id = "main_app"),+ #' |
|||
261 | -! | +|||
409 | +
- shinyjs::hidden(+ cdisc_join_keys <- function(...) { |
|||
262 | -! | +|||
410 | +61x |
- tags$div(+ data_objects <- list(...) |
||
263 | -! | +|||
411 | +
- id = "data_loaded",+ |
|||
264 | -! | +|||
412 | +61x |
- div(+ data_objects_parsed <- lapply(seq_along(data_objects), function(ix) { |
||
265 | -! | +|||
413 | +79x |
- h3("Data successfully loaded."),+ item <- data_objects[[ix]] |
||
266 | -! | +|||
414 | +
- p("You can close this window and get back to R console.")+ |
|||
267 | -+ | |||
415 | +79x |
- )+ name <- names(data_objects)[ix] |
||
268 | -+ | |||
416 | +76x |
- )+ if (is.null(name) || identical(trimws(name), "")) name <- item # fallback to value if names are not set |
||
269 | +417 |
- ),+ |
||
270 | -! | +|||
418 | +61x |
- include_js_files(),+ if ( |
||
271 | -! | +|||
419 | +79x |
- br()+ checkmate::test_r6(item) && |
||
272 | -+ | |||
420 | +79x |
- )+ checkmate::test_multi_class( |
||
273 | -+ | |||
421 | +79x |
- )+ item, |
||
274 | -+ | |||
422 | +79x |
- ),+ classes = c("TealDataConnector", "TealDataset", "TealDatasetConnector") |
||
275 | -! | +|||
423 | +
- server = function(input, output, session) {+ ) |
|||
276 | -! | +|||
424 | +
- session$onSessionEnded(stopApp)+ ) { |
|||
277 | -! | +|||
425 | +
- dat <- self$get_server()(id = "main_app")+ # Code not refactored for these data types as they'll be deprecated soon |
|||
278 | +426 |
-
+ # see logic in function `deprecated_join_keys_extract` called under `cdisc_data` |
||
279 | -! | +|||
427 | +34x |
- observeEvent(dat(), {+ return(NULL) |
||
280 | -! | +|||
428 | +61x |
- if (self$is_pulled()) {+ } else if ( |
||
281 | -! | +|||
429 | +45x |
- shinyjs::show("data_loaded")+ checkmate::test_class(item, "JoinKeySet") || |
||
282 | -! | +|||
430 | +45x |
- stopApp()+ !checkmate::test_string(name, min.chars = 1) || |
||
283 | -+ | |||
431 | +45x |
- }+ !name %in% names(default_cdisc_keys) |
||
284 | +432 |
- })+ ) { |
||
285 | +433 | ! |
- NULL+ return(list(item)) |
|
286 | +434 |
- }+ } |
||
287 | +435 |
- )+ |
||
288 | +436 |
- },+ # Add primary key+ |
+ ||
437 | +45x | +
+ result <- list(join_key(name, keys = get_cdisc_keys(name)))+ |
+ ||
438 | +45x | +
+ keys_list <- default_cdisc_keys[[name]] |
||
289 | +439 | |||
290 | -+ | |||
440 | +45x |
- # ___ mutate ====+ if (is.null(keys_list) || is.null(keys_list$parent) || is.null(keys_list$foreign)) { |
||
291 | -+ | |||
441 | +5x |
- #' @description+ return(result) |
||
292 | +442 |
- #' Change join_keys for a given pair of dataset names+ } |
||
293 | +443 |
- #' @param dataset_1,dataset_2 (`character`) datasets for which join_keys are to be returned+ # Add JoinKey with parent dataset (if exists)+ |
+ ||
444 | +40x | +
+ append(result, list(join_key(name, keys_list$parent, keys = keys_list$foreign))) |
||
294 | +445 |
- #' @param val (named `character`) column names used to join+ }) |
||
295 | +446 |
- #' @return (`self`) invisibly for chaining+ + |
+ ||
447 | +61x | +
+ data_objects_parsed <- do.call(c, data_objects_parsed) |
||
296 | +448 |
- mutate_join_keys = function(dataset_1, dataset_2, val) {+ |
||
297 | -3x | +449 | +61x |
- private$join_keys$mutate(dataset_1, dataset_2, val)+ do.call(join_keys, as.list(data_objects_parsed[!is.null(data_objects_parsed)])) |
298 | +450 |
- },+ } |
||
299 | +451 | |||
300 | +452 |
- # ___ check ====+ # wrappers ==== |
||
301 | +453 |
- #' @description+ #' Mutate `JoinKeys` with a new values |
||
302 | +454 |
- #' Check there is consistency between the datasets and join_keys+ #' |
||
303 | +455 |
- #' @return raise and error or invisible `TRUE`+ #' @description `r lifecycle::badge("experimental")` |
||
304 | +456 |
- check_metadata = function() {+ #' Mutate `JoinKeys` with a new values |
||
305 | -52x | +|||
457 | +
- if (isFALSE(self$is_pulled())) {+ #' |
|||
306 | +458 |
- # all the checks below required data to be already pulled+ #' @param x (`JoinKeys`) object to be modified |
||
307 | -4x | +|||
459 | +
- return(invisible(TRUE))+ #' @param dataset_1 (`character`) one dataset name |
|||
308 | +460 |
- }+ #' @param dataset_2 (`character`) other dataset name |
||
309 | +461 |
-
+ #' @param val (named `character`) column names used to join |
||
310 | -48x | +|||
462 | +
- for (dataset in self$get_datasets()) {+ #' |
|||
311 | -82x | +|||
463 | +
- dataname <- get_dataname(dataset)+ #' @return modified `JoinKeys` object |
|||
312 | -82x | +|||
464 | +
- dataset_colnames <- dataset$get_colnames()+ #' |
|||
313 | +465 |
-
+ #' @export |
||
314 | +466 |
- # expected columns in this dataset from JoinKeys specification+ mutate_join_keys <- function(x, dataset_1, dataset_2, val) { |
||
315 | -82x | +|||
467 | +! |
- join_key_cols <- unique(unlist(lapply(self$get_join_keys(dataname), names)))+ UseMethod("mutate_join_keys") |
||
316 | -82x | +|||
468 | +
- if (!is.null(join_key_cols) && !all(join_key_cols %in% dataset_colnames)) {+ } |
|||
317 | -3x | +|||
469 | +
- stop(+ |
|||
318 | -3x | +|||
470 | +
- paste(+ #' @rdname mutate_join_keys |
|||
319 | -3x | +|||
471 | +
- "The join key specification requires dataset",+ #' @export |
|||
320 | -3x | +|||
472 | +
- dataname,+ #' @examples |
|||
321 | -3x | +|||
473 | +
- "to contain the following columns:",+ #' # JoinKeys ---- |
|||
322 | -3x | +|||
474 | +
- paste(join_key_cols, collapse = ", ")+ #' |
|||
323 | +475 |
- )+ #' x <- join_keys( |
||
324 | +476 |
- )+ #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")), |
||
325 | +477 |
- }+ #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y")) |
||
326 | +478 |
-
+ #' ) |
||
327 | +479 |
- # check if primary keys in dataset+ #' x$get("dataset_A", "dataset_B") |
||
328 | -79x | +|||
480 | +
- primary_key_cols <- self$get_join_keys(dataname, dataname)+ #' |
|||
329 | -79x | +|||
481 | +
- if (!is.null(primary_key_cols) && !all(primary_key_cols %in% dataset_colnames)) {+ #' mutate_join_keys(x, "dataset_A", "dataset_B", c("col_1" = "col_10")) |
|||
330 | -! | -
- stop(- |
- ||
331 | -! | -
- paste(- |
- ||
332 | -! | -
- "The primary keys specification requires dataset",- |
- ||
333 | -! | +|||
482 | +
- dataname,+ #' x$get("dataset_A", "dataset_B") |
|||
334 | -! | +|||
483 | +
- "to contain the following columns:",+ mutate_join_keys.JoinKeys <- function(x, dataset_1, dataset_2, val) { |
|||
335 | +484 | ! |
- paste(primary_key_cols, collapse = ", ")+ x$mutate(dataset_1, dataset_2, val) |
|
336 | +485 |
- )+ } |
||
337 | +486 |
- )+ |
||
338 | +487 |
- }+ #' @rdname mutate_join_keys |
||
339 | -79x | +|||
488 | +
- dataset$check_keys()+ #' @export |
|||
340 | +489 |
- }+ #' @examples |
||
341 | +490 |
-
+ #' # TealData ---- |
||
342 | -43x | +|||
491 | +
- logger::log_trace("TealData$check_metadata metadata check passed.")+ #' |
|||
343 | +492 |
-
+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
344 | -43x | +|||
493 | +
- return(invisible(TRUE))+ #' ADRS <- teal.data::example_cdisc_data("ADRS") |
|||
345 | +494 |
- }+ #' |
||
346 | +495 |
- ),+ #' x <- cdisc_data( |
||
347 | +496 |
-
+ #' cdisc_dataset("ADSL", ADSL), |
||
348 | +497 |
- ## __Private Fields ====+ #' cdisc_dataset("ADRS", ADRS) |
||
349 | +498 |
- private = list(+ #' ) |
||
350 | +499 |
- join_keys = NULL,+ #' x$get_join_keys()$get("ADSL", "ADRS") |
||
351 | +500 |
- ui = function(id) {+ #' |
||
352 | -4x | +|||
501 | +
- ns <- NS(id)+ #' mutate_join_keys(x, "ADSL", "ADRS", c("COLUMN1" = "COLUMN2")) |
|||
353 | +502 |
-
+ #' x$get_join_keys()$get("ADSL", "ADRS") |
||
354 | +503 |
- # connectors ui(s) + submit button+ mutate_join_keys.TealData <- function(x, dataset_1, dataset_2, val) { # nolint |
||
355 | -4x | +|||
504 | +! |
- fluidPage(+ x$mutate_join_keys(dataset_1, dataset_2, val) |
||
356 | -4x | +|||
505 | +
- include_js_files(),+ } |
|||
357 | -4x | +|||
506 | +
- theme = get_teal_bs_theme(),+ |
|||
358 | -4x | +|||
507 | +
- shinyjs::hidden(+ #' Create a relationship between a pair of datasets |
|||
359 | -4x | +|||
508 | +
- column(+ #' |
|||
360 | -4x | +|||
509 | +
- id = ns("delayed_data"),+ #' @description `r lifecycle::badge("stable")` |
|||
361 | -4x | +|||
510 | +
- width = 8,+ #' |
|||
362 | -4x | +|||
511 | +
- offset = 2,+ #' @details `join_key()` will create a relationship for the variables on a pair |
|||
363 | -4x | +|||
512 | +
- div(+ #' of datasets. |
|||
364 | -4x | +|||
513 | +
- tagList(+ #' |
|||
365 | -4x | +|||
514 | +
- lapply(+ #' @inheritParams mutate_join_keys |
|||
366 | -4x | +|||
515 | +
- private$datasets,+ #' @param dataset_2 (`character`) other dataset name. In case it is omitted, then it |
|||
367 | -4x | +|||
516 | +
- function(x) {+ #' will create a primary key for `dataset_1`. |
|||
368 | -6x | +|||
517 | +
- div(+ #' @param keys (optionally named `character`) where `names(keys)` are columns in `dataset_1` |
|||
369 | -6x | +|||
518 | +
- if (inherits(x, "TealDataConnector")) {+ #' with relationship to columns of `dataset_2` given by the elements in `keys`. |
|||
370 | -! | +|||
519 | +
- ui <- x$get_ui(id = ns(x$id))+ #' If `names(keys)` is `NULL` then the same column names are used for both `dataset_1` |
|||
371 | -! | +|||
520 | +
- if (is.null(ui)) {+ #' and `dataset_2`. |
|||
372 | -! | +|||
521 | +
- ui <- div(+ #' |
|||
373 | -! | +|||
522 | +
- h4("TealDataset Connector for: ", lapply(x$get_datanames(), code)),+ #' @return object of class `JoinKeySet` to be passed into `join_keys` function. |
|||
374 | -! | +|||
523 | +
- p(icon("check"), "Ready to Load")+ #' |
|||
375 | +524 |
- )+ #' @seealso [join_keys()] |
||
376 | +525 |
- }+ #' |
||
377 | -! | +|||
526 | +
- ui+ #' @export |
|||
378 | -6x | +|||
527 | +
- } else if (inherits(x, "TealDatasetConnector")) {+ join_key <- function(dataset_1, dataset_2 = NULL, keys) { |
|||
379 | -! | +|||
528 | +557x |
- ui <- x$get_ui(id = ns(paste0(x$get_datanames(), collapse = "_")))+ checkmate::assert_string(dataset_1) |
||
380 | -! | +|||
529 | +557x |
- if (is.null(ui)) {+ checkmate::assert_string(dataset_2, null.ok = TRUE) |
||
381 | -! | +|||
530 | +554x |
- ui <- div(+ checkmate::assert_character(keys, any.missing = FALSE) |
||
382 | -! | +|||
531 | +
- h4("TealDataset Connector for: ", code(x$get_dataname())),+ |
|||
383 | -! | +|||
532 | +552x |
- p(icon("check"), "Ready to Load")+ dataset_2 <- dataset_2 %||% dataset_1 |
||
384 | +533 |
- )+ |
||
385 | -+ | |||
534 | +552x |
- }+ if (length(keys) > 0) { |
||
386 | -! | +|||
535 | +508x |
- ui+ if (is.null(names(keys))) { |
||
387 | -+ | |||
536 | +219x |
- } else {+ names(keys) <- keys |
||
388 | -6x | +|||
537 | +
- div(h4("Data(set) for: ", lapply(x$get_datanames(), code)), p(icon("check"), "Loaded"))+ } |
|||
389 | +538 |
- },+ |
||
390 | -6x | +539 | +508x |
- br()+ if (any(names(keys) == "")) { |
391 | -+ | |||
540 | +4x |
- )+ names(keys)[names(keys) == "" & keys != ""] <- keys[names(keys) == "" & keys != ""] |
||
392 | +541 |
- }+ } |
||
393 | +542 |
- ),+ |
||
394 | -4x | +543 | +508x |
- actionButton(inputId = ns("submit"), label = "Submit all")+ stopifnot(!is.null(names(keys))) |
395 | -+ | |||
544 | +508x |
- ),+ stopifnot(!anyDuplicated(keys)) |
||
396 | -4x | +545 | +507x |
- `data-proxy-click` = ns("submit") # handled by jscode in custom.js - hit enter to submit+ stopifnot(!anyDuplicated(names(keys))) |
397 | +546 |
- )+ } |
||
398 | +547 |
- )+ |
||
399 | -+ | |||
548 | +550x |
- )+ if (dataset_1 == dataset_2 && any(names(keys) != keys)) { |
||
400 | -+ | |||
549 | +2x |
- )+ stop("Keys within a dataset must match exactly: keys = c('A' = 'B') are not allowed") |
||
401 | +550 |
- },+ } |
||
402 | +551 |
- server = function(input, output, session) {+ |
||
403 | -! | +|||
552 | +548x |
- logger::log_trace("TealData$server initializing...")+ structure( |
||
404 | -+ | |||
553 | +548x |
-
+ list( |
||
405 | -! | +|||
554 | +548x |
- shinyjs::show("delayed_data")+ dataset_1 = dataset_1, |
||
406 | -! | +|||
555 | +548x |
- for (dc in self$get_connectors()) {+ dataset_2 = dataset_2, |
||
407 | -! | +|||
556 | +548x |
- if (inherits(dc, "TealDataConnector")) {- |
- ||
408 | -! | -
- dc$get_preopen_server()(id = dc$id)- |
- ||
409 | -- |
- }- |
- ||
410 | -- |
- }- |
- ||
411 | -! | -
- rv <- reactiveVal(NULL)- |
- ||
412 | -! | -
- observeEvent(input$submit, {- |
- ||
413 | -! | -
- logger::log_trace("TealData$server@1 submit button clicked.")- |
- ||
414 | -- |
- # load data from all connectors- |
- ||
415 | -! | -
- for (dc in self$get_connectors()) {- |
- ||
416 | -! | -
- if (inherits(dc, "TealDataConnector")) {- |
- ||
417 | -! | -
- dc$get_server()(- |
- ||
418 | -! | -
- id = dc$id,- |
- ||
419 | -! | -
- connection = dc$get_connection(),- |
- ||
420 | -! | -
- connectors = dc$get_items()- |
- ||
421 | -- |
- )- |
- ||
422 | -! | -
- } else if (inherits(dc, "TealDatasetConnector")) {- |
- ||
423 | -! | -
- dc$get_server()(id = dc$get_dataname())- |
- ||
424 | -- |
- }- |
- ||
425 | -! | -
- if (dc$is_failed()) {- |
- ||
426 | -! | -
- break- |
- ||
427 | -- |
- }- |
- ||
428 | -- |
- }- |
- ||
429 | -- | - - | -||
430 | -! | -
- if (self$is_pulled()) {- |
- ||
431 | -! | -
- logger::log_trace("TealData$server@1 data is pulled.")- |
- ||
432 | -! | -
- withProgress(value = 1, message = "Checking data reproducibility", {- |
- ||
433 | -- |
- # We check first and then mutate.- |
- ||
434 | -- |
- # mutate_code is reproducible by default we assume that we don't- |
- ||
435 | -- |
- # have to check the result of the re-evaluation of the code- |
- ||
436 | -! | -
- self$check_reproducibility()- |
- ||
437 | -- |
- })- |
- ||
438 | -- | - - | -||
439 | -! | -
- withProgress(value = 1, message = "Executing processing code", {- |
- ||
440 | -! | -
- self$execute_mutate()- |
- ||
441 | -! | -
- self$check_metadata()- |
- ||
442 | -- |
- })- |
- ||
443 | -! | -
- logger::log_info("Data ready to pass to the application.")- |
- ||
444 | -! | -
- shinyjs::hide("delayed_data")- |
- ||
445 | -! | -
- rv(self)- |
- ||
446 | -- |
- }+ keys = keys |
||
447 | +557 |
- })- |
- ||
448 | -! | -
- return(rv)+ ), |
||
449 | -+ | |||
558 | +548x |
- }+ class = "JoinKeySet" |
||
450 | +559 |
) |
||
451 | +560 |
- )+ } |
1 |
- ## JoinKeys ====+ #' Create a new `TealDatasetConnector` object |
||
3 |
- #'+ #' `r lifecycle::badge("stable")` |
||
4 |
- #' @title R6 Class to store relationships for joining datasets+ #' |
||
5 |
- #'+ #' Create `TealDatasetConnector` from [callable_function]. |
||
6 |
- #' @description `r lifecycle::badge("stable")`+ #' |
||
7 |
- #' This class stores symmetric links between pairs of key-values+ #' @param dataname (`character`)\cr |
||
8 |
- #' (e.g. column A of dataset X can be joined with column B of dataset Y). This relationship+ #' A given name for the dataset it may not contain spaces |
||
9 |
- #' is more general than the SQL foreign key relationship which also imposes constraints on the values+ #' |
||
10 |
- #' of these columns.+ #' @param pull_callable (`CallableFunction`)\cr |
||
11 |
- #' @param dataset_1 (`character`) one dataset name+ #' function with necessary arguments set to fetch data from connection. |
||
12 |
- #' @param dataset_2 (`character`) other dataset name+ #' |
||
13 |
- #'+ #' @param keys optional, (`character`)\cr |
||
14 |
- #' @examples+ #' vector of dataset primary keys column names |
||
15 |
- #' x <- teal.data:::JoinKeys$new()+ #' |
||
16 |
- #' x$set(+ #' @param label (`character`)\cr |
||
17 |
- #' list(+ #' Label to describe the dataset. |
||
18 |
- #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")),+ #' |
||
19 |
- #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y"))+ #' @param code (`character`)\cr |
||
20 |
- #' )+ #' A character string defining code to modify `raw_data` from this dataset. To modify |
||
21 |
- #' )+ #' current dataset code should contain at least one assignment to object defined in `dataname` |
||
22 |
- #' x$get()+ #' argument. For example if `dataname = ADSL` example code should contain |
||
23 |
- #' x$mutate("dataset_A", "dataset_B", c("col1" = "col10"))+ #' `ADSL <- <some R code>`. Can't be used simultaneously with `script` |
||
24 |
- #' x$get("dataset_A", "dataset_B")+ #' |
||
25 |
- JoinKeys <- R6::R6Class( # nolint+ #' @param script (`character`)\cr |
||
26 |
- classname = "JoinKeys",+ #' Alternatively to `code` - location of the file containing modification code. |
||
27 |
- ## __Public Methods ====+ #' Can't be used simultaneously with `script`. |
||
28 |
- public = list(+ #' |
||
29 |
- #' @description+ #' @param vars (named `list`)) \cr |
||
30 |
- #' Create a new object of `JoinKeys`+ #' In case when this object code depends on other `TealDataset` object(s) or |
||
31 |
- #' @return empty (`JoinKeys`)+ #' other constant value, this/these object(s) should be included as named |
||
32 |
- initialize = function() {+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
33 | -308x | +
- logger::log_trace("JoinKeys initialized.")+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
|
34 | -308x | +
- return(invisible(self))+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
|
35 |
- },+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
||
36 |
- #' @description+ #' are included to this object as local `vars` and they cannot be modified |
||
37 |
- #' Split the current `JoinKeys` object into a named list of join keys objects with an element for each dataset+ #' within another dataset. |
||
38 |
- #' @return (`list`) a list of `JoinKeys` object+ #' |
||
39 |
- split = function() {+ #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr |
||
40 | -6x | +
- list_of_list_of_join_key_set <- lapply(+ #' Field containing either the metadata about the dataset (each element of the list |
|
41 | -6x | +
- names(self$get()),+ #' should be atomic and length one) or a `CallableFuntion` to pull the metadata |
|
42 | -6x | +
- function(dataset_1) {+ #' from a connection. This should return a `list` or an object which can be |
|
43 | -27x | +
- lapply(+ #' converted to a list with `as.list`. |
|
44 | -27x | +
- names(self$get()[[dataset_1]]),+ #' @return new `TealDatasetConnector` object |
|
45 | -27x | +
- function(dataset_2) join_key(dataset_1, dataset_2, self$get()[[dataset_1]][[dataset_2]])+ #' |
|
46 |
- )+ #' @examples |
||
47 |
- }+ #' library(MultiAssayExperiment) |
||
48 |
- )+ #' # data.frame example |
||
49 | -6x | +
- res <- lapply(+ #' pull_fun2 <- callable_function(data.frame) |
|
50 | -6x | +
- list_of_list_of_join_key_set,+ #' pull_fun2$set_args(args = list(a = c(1, 2, 3))) |
|
51 | -6x | +
- function(x) {+ #' dataset_connector("test", pull_fun2) |
|
52 | -27x | +
- y <- JoinKeys$new()+ #' |
|
53 | -27x | +
- y$set(x)+ #' # MultiAssayExperiment example |
|
54 |
- }+ #' pull_fun <- callable_function( |
||
55 |
- )+ #' function() { |
||
56 | -6x | +
- names(res) <- names(self$get())+ #' library("MultiAssayExperiment") |
|
57 |
-
+ #' data("miniACC") |
||
58 | -6x | +
- logger::log_trace("JoinKeys$split keys split.")+ #' return(miniACC) |
|
59 | -6x | +
- return(res)+ #' } |
|
60 |
- },+ #' ) |
||
61 |
- #' @description+ #' dataset_connector( |
||
62 |
- #' Merging a list (or one) of `JoinKeys` objects into the current `JoinKeys` object+ #' "miniacc", |
||
63 |
- #' @param x `list` of `JoinKeys` objects or single `JoinKeys` object+ #' pull_fun, |
||
64 |
- #' @return (`self`) invisibly for chaining+ #' code = 'library("MultiAssayExperiment"); data("miniACC"); return(miniACC)' |
||
65 |
- merge = function(x) {+ #' ) |
||
66 | -5x | +
- if (inherits(x, "JoinKeys")) x <- list(x)+ #' @export |
|
67 | -18x | +
- checkmate::assert_list(x, types = "JoinKeys", min.len = 1)+ dataset_connector <- function(dataname, |
|
68 | -13x | +
- for (jk in x) {+ pull_callable, |
|
69 | -25x | +
- for (dataset_1 in names(jk$get())) {+ keys = character(0), |
|
70 | -87x | +
- for (dataset_2 in names(jk$get()[[dataset_1]])) {+ label = character(0), |
|
71 | -102x | +
- self$mutate(dataset_1, dataset_2, jk$get()[[dataset_1]][[dataset_2]])+ code = character(0), |
|
72 |
- }+ script = character(0), |
||
73 |
- }+ vars = list(), |
||
74 |
- }+ metadata = NULL) { |
||
75 | -13x | +112x |
- logger::log_trace("JoinKeys$merge keys merged.")+ checkmate::assert_string(dataname) |
76 | -13x | +111x |
- return(invisible(self))+ stopifnot(inherits(pull_callable, "Callable")) |
77 | -+ | 111x |
- },+ checkmate::assert_character(keys, any.missing = FALSE) |
78 | -+ | 111x |
- #' @description+ checkmate::assert_character(code, any.missing = FALSE) |
79 | -+ | 111x |
- #' Get join keys between two datasets.+ checkmate::assert_character(label, any.missing = FALSE) |
80 |
- #' @return (`character`) named character vector x with names(x) the+ |
||
81 | -+ | 111x |
- #' columns of `dataset_1` and the values of `(x)` the corresponding join+ if (!checkmate::test_class(metadata, "Callable", null.ok = TRUE)) { |
82 | -+ | 14x |
- #' keys in `dataset_2` or `character(0)` if no relationship+ validate_metadata(metadata) |
83 |
- #' @details if one or both of `dataset_1` and `dataset_2` are missing then+ } |
||
84 |
- #' underlying keys structure is returned for further processing+ |
||
85 | -+ | 111x |
- get = function(dataset_1, dataset_2) {+ x <- TealDatasetConnector$new( |
86 | -994x | +111x |
- if (missing(dataset_1) && missing(dataset_2)) {+ dataname = dataname, |
87 | -375x | +111x |
- return(private$.keys)+ pull_callable = pull_callable, |
88 | -+ | 111x |
- }+ keys = keys, |
89 | -619x | +111x |
- if (missing(dataset_2)) {+ code = code_from_script(code, script), |
90 | -95x | +111x |
- return(private$.keys[[dataset_1]])+ label = label, |
91 | -+ | 111x |
- }+ vars = vars, |
92 | -524x | +111x |
- if (missing(dataset_1)) {+ metadata = metadata |
93 | -1x | +
- return(private$.keys[[dataset_2]])+ ) |
|
94 |
- }+ |
||
95 | -523x | +111x |
- if (is.null(private$.keys[[dataset_1]][[dataset_2]])) {+ return(x) |
96 | -153x | +
- return(character(0))+ } |
|
97 |
- }+ |
||
98 | -370x | +
- return(private$.keys[[dataset_1]][[dataset_2]])+ #' Create a new `CDISCTealDatasetConnector` object |
|
99 |
- },+ #' |
||
100 |
- #' @description+ #' `r lifecycle::badge("stable")` |
||
101 |
- #' Change join_keys for a given pair of dataset names (or+ #' |
||
102 |
- #' add join_keys for given pair if it does not exist)+ #' Create `CDISCTealDatasetConnector` from [callable_function]. |
||
103 |
- #' @param val (named `character`) column names used to join+ #' |
||
104 |
- #' @return (`self`) invisibly for chaining+ #' @inheritParams dataset_connector |
||
105 |
- mutate = function(dataset_1, dataset_2, val) {+ #' @inheritParams cdisc_dataset |
||
106 | -236x | +
- checkmate::assert_string(dataset_1)+ #' |
|
107 | -236x | +
- checkmate::assert_string(dataset_2)+ #' @return new `CDISCTealDatasetConnector` object |
|
108 | -236x | +
- checkmate::assert_character(val, any.missing = FALSE)+ #' |
|
109 |
-
+ #' @export |
||
110 | -236x | +
- private$join_pair(join_key(dataset_1, dataset_2, val))+ cdisc_dataset_connector <- function(dataname, |
|
111 |
-
+ pull_callable, |
||
112 | -236x | +
- logger::log_trace(+ keys, |
|
113 | -236x | +
- sprintf(+ parent = `if`(identical(dataname, "ADSL"), character(0), "ADSL"), |
|
114 | -236x | +
- "JoinKeys$mutate updated the keys between %s and %s to %s",+ label = character(0), |
|
115 | -236x | +
- dataset_1,+ code = character(0), |
|
116 | -236x | +
- dataset_2,+ script = character(0), |
|
117 | -236x | +
- paste(val, collapse = ", ")+ vars = list(), |
|
118 |
- )+ metadata = NULL) { |
||
119 | -+ | 32x |
- )+ checkmate::assert_string(dataname) |
120 | -236x | +32x |
- return(invisible(self))+ stopifnot(inherits(pull_callable, "Callable")) |
121 | -+ | 32x |
- },+ checkmate::assert_character(keys, any.missing = FALSE) |
122 | -+ | 32x |
- #' @description+ checkmate::assert_character(parent, max.len = 1, any.missing = FALSE) |
123 | -+ | 32x |
- #' Set up join keys basing on list of `JoinKeySet` objects.+ checkmate::assert_character(code, max.len = 1, any.missing = FALSE) |
124 | -+ | 32x |
- #' @param x `list` of `JoinKeySet` objects (which are created using the `join_key` function)+ checkmate::assert_character(label, max.len = 1, any.missing = FALSE) |
125 |
- #' or single `JoinKeySet` objects+ |
||
126 | -+ | 32x |
- #' @details Note that join keys are symmetric although the relationship only needs+ if (!checkmate::test_class(metadata, "Callable", null.ok = TRUE)) { |
127 | -+ | 11x |
- #' to be specified once+ validate_metadata(metadata) |
128 |
- #' @return (`self`) invisibly for chaining+ } |
||
129 |
- set = function(x) {+ |
||
130 | -169x | +32x |
- if (length(private$.keys) > 0) {+ x <- CDISCTealDatasetConnector$new( |
131 | -1x | +32x |
- stop("Keys already set, please use JoinKeys$mutate() to change them")+ dataname = dataname, |
132 | -+ | 32x |
- }+ pull_callable = pull_callable, |
133 | -168x | +32x |
- if (inherits(x, "JoinKeySet")) {+ keys = keys, |
134 | -! | +32x |
- x <- list(x)+ parent = parent, |
135 | -+ | 32x |
- }+ code = code_from_script(code, script), |
136 | -+ | 32x |
-
+ label = label, |
137 | -+ | 32x |
- # check if any JoinKeySets share the same datasets but different values+ vars = vars, |
138 | -168x | +32x |
- for (idx_1 in seq_along(x)) {+ metadata = metadata |
139 | -306x | +
- for (idx_2 in seq_len(idx_1)) {+ ) |
|
140 | -502x | +
- private$check_compatible_keys(x[[idx_1]], x[[idx_2]])+ |
|
141 | -+ | 32x |
- }+ return(x) |
142 |
- }+ } |
||
144 | -160x | +
- checkmate::assert_list(x, types = "JoinKeySet", min.len = 1)+ |
|
145 | -160x | +
- lapply(x, private$join_pair)+ #' Load `TealDatasetConnector` object from a file |
|
146 |
-
+ #' |
||
147 | -160x | +
- logger::log_trace("JoinKeys$set keys are set.")+ #' `r lifecycle::badge("stable")` |
|
148 | -160x | +
- return(invisible(self))+ #' |
|
149 |
- },+ #' Please note that the script has to end with a call creating desired object. The error will |
||
150 |
- #' @description+ #' be raised otherwise. |
||
151 |
- #' Prints this `JoinKeys`.+ #' |
||
152 |
- #'+ #' @inheritParams dataset_file |
||
153 |
- #' @param ... additional arguments to the printing method+ #' |
||
154 |
- #' @return invisibly self+ #' @return `TealDatasetConnector` object |
||
155 |
- print = function(...) {+ #' |
||
156 | -2x | +
- check_ellipsis(...)+ #' @rdname dataset_connector_file |
|
157 | -2x | +
- keys_list <- self$get()+ #' |
|
158 | -2x | +
- if (length(keys_list) > 0) {+ #' @export |
|
159 | -1x | +
- cat(sprintf(+ #' |
|
160 | -1x | +
- "A JoinKeys object containing foreign keys between %s datasets:\n",+ #' @examples |
|
161 | -1x | +
- length(keys_list)+ #' # simple example |
|
162 |
- ))+ #' library(magrittr) |
||
163 | -1x | +
- print(keys_list)+ #' file_example <- tempfile(fileext = ".R") |
|
164 |
- } else {+ #' writeLines( |
||
165 | -1x | +
- cat("An empty JoinKeys object.")+ #' text = c( |
|
166 |
- }+ #' "library(teal.data) |
||
167 | -2x | +
- invisible(self)+ #' |
|
168 |
- },+ #' pull_callable <- callable_function(teal.data::example_cdisc_data) %>% |
||
169 |
- #' @description+ #' set_args(list(dataname = \"ADSL\")) |
||
170 |
- #' Sets the parents of the datasets.+ #' dataset_connector(\"ADSL\", pull_callable, get_cdisc_keys(\"ADSL\"))" |
||
171 |
- #'+ #' ), |
||
172 |
- #' @param named_list Named (`list`) of the parents datasets.+ #' con = file_example |
||
173 |
- #'+ #' ) |
||
174 |
- #' @return (`self`) invisibly for chaining+ #' x <- dataset_connector_file(file_example) |
||
175 |
- set_parents = function(named_list) {+ #' get_code(x) |
||
176 | -34x | +
- for (dataset in names(named_list)) {+ dataset_connector_file <- function(path) { # nolint |
|
177 | -74x | +! |
- checkmate::assert(+ object <- object_file(path, "TealDatasetConnector") |
178 | -74x | +! |
- checkmate::check_null(self$get_parent(dataset)),+ return(object) |
179 | -74x | +
- checkmate::check_true(+ } |
|
180 | -74x | +
- length(self$get_parent(dataset)) == 0 &&+ |
|
181 | -74x | +
- length(named_list[[dataset]]) == 0+ #' Load `CDISCTealDatasetConnector` object from a file |
|
182 |
- ),+ #' |
||
183 | -74x | +
- checkmate::check_true(self$get_parent(dataset) == named_list[[dataset]]),+ #' `r lifecycle::badge("stable")` |
|
184 | -74x | +
- "Please check the difference between provided datasets parents and provided join_keys parents."+ #' |
|
185 |
- )+ #' Please note that the script has to end with a call creating desired object. The error will |
||
186 | -73x | +
- if (is.null(self$get_parent(dataset))) {+ #' be raised otherwise. |
|
187 | -70x | +
- private$parents[[dataset]] <- named_list[[dataset]]+ #' |
|
188 |
- }+ #' @inheritParams dataset_connector_file |
||
189 |
- }+ #' |
||
190 | -33x | +
- invisible(self)+ #' @return `CDISCTealDatasetConnector` object |
|
191 |
- },+ #' |
||
192 |
- #' @description+ #' @rdname dataset_connector_file |
||
193 |
- #' Gets the parent of the desired dataset.+ #' |
||
194 |
- #'+ #' @export |
||
195 |
- #' @param dataname (`character`) name of the dataset.+ #' |
||
196 |
- #' @return (`character`) the parent of the desired dataset+ #' @examples |
||
197 |
- get_parent = function(dataname) {+ #' # simple example |
||
198 | -241x | +
- if (missing(dataname)) {+ #' library(magrittr) |
|
199 | -1x | +
- return(NULL)+ #' file_example <- tempfile(fileext = ".R") |
|
200 |
- }+ #' writeLines( |
||
201 | -240x | +
- private$parents[[dataname]]+ #' text = c( |
|
202 |
- },+ #' "library(teal.data) |
||
203 |
- #' @description+ #' |
||
204 |
- #' Gets the parents of the datasets.+ #' pull_callable <- callable_function(teal.data::example_cdisc_data) %>% |
||
205 |
- #'+ #' set_args(list(dataname = \"ADSL\")) |
||
206 |
- #' @return (`list`) A named list of the parents of all datasets+ #' cdisc_dataset_connector(\"ADSL\", pull_callable, get_cdisc_keys(\"ADSL\"))" |
||
207 |
- get_parents = function() {+ #' ), |
||
208 | -53x | +
- private$parents+ #' con = file_example |
|
209 |
- },+ #' ) |
||
210 |
- #' @description+ #' x <- cdisc_dataset_connector_file(file_example) |
||
211 |
- #' Updates the keys of the datasets based on the parents.+ #' get_code(x) |
||
212 |
- #'+ cdisc_dataset_connector_file <- function(path) { # nolint |
||
213 | -+ | ! |
- #' @return (`self`) invisibly for chaining+ object <- object_file(path, "CDISCTealDatasetConnector") |
214 | -+ | ! |
- update_keys_given_parents = function() {+ return(object) |
215 | -22x | +
- datanames <- names(self$get())+ } |
|
216 | -22x | +
- duplicate_pairs <- list()+ |
|
217 | -22x | +
- for (d1 in datanames) {+ # RDS ==== |
|
218 | -46x | +
- d1_pk <- self$get(d1, d1)+ #' `RDS` `TealDatasetConnector` |
|
219 | -46x | +
- d1_parent <- self$get_parent(d1)+ #' |
|
220 | -46x | +
- for (d2 in datanames) {+ #' `r lifecycle::badge("stable")` |
|
221 | -112x | +
- if (paste(d2, d1) %in% duplicate_pairs) {+ #' |
|
222 | -26x | +
- next+ #' Create a `TealDatasetConnector` from `RDS` file. |
|
223 |
- }+ #' |
||
224 | -86x | +
- if (length(self$get(d1, d2)) == 0) {+ #' @inheritParams dataset_connector |
|
225 | -38x | +
- d2_parent <- self$get_parent(d2)+ #' @inheritParams fun_dataset_connector |
|
226 | -38x | +
- d2_pk <- self$get(d2, d2)+ #' @param file (`character`)\cr |
|
227 |
-
+ #' path to (`.rds` or `.R`) that contains `data.frame` object or |
||
228 | -38x | +
- fk <- if (identical(d1, d2_parent)) {+ #' code to `source` |
|
229 |
- # first is parent of second -> parent keys -> first keys+ #' |
||
230 | -18x | +
- d1_pk+ #' @param ... (`optional`)\cr |
|
231 | -38x | +
- } else if (identical(d1_parent, d2)) {+ #' additional arguments applied to [base::readRDS()] function |
|
232 |
- # second is parent of first -> parent keys -> second keys+ #' |
||
233 | -! | +
- d2_pk+ #' @export |
|
234 | -38x | +
- } else if (identical(d1_parent, d2_parent) && length(d1_parent) > 0) {+ #' |
|
235 |
- # both has the same parent -> parent keys+ #' @rdname rds_dataset_connector |
||
236 | -10x | +
- self$get(d1_parent, d1_parent)+ #' |
|
237 |
- } else {+ #' @examples |
||
238 |
- # cant find connection - leave empty+ #' \dontrun{ |
||
239 | -10x | +
- next+ #' x <- rds_dataset_connector( |
|
240 |
- }+ #' dataname = "ADSL", |
||
241 | -28x | +
- self$mutate(d1, d2, fk)+ #' file = "path/to/file.RDS" |
|
242 | -28x | +
- duplicate_pairs <- append(duplicate_pairs, paste(d1, d2))+ #' ) |
|
243 |
- }+ #' x$get_code() |
||
244 |
- }+ #' } |
||
245 |
- }+ rds_dataset_connector <- function(dataname, |
||
246 |
- # check parent child relation+ file, |
||
247 | -22x | +
- private$check_parent_child()+ keys = character(0), |
|
248 |
-
+ label = character(0), |
||
249 | -22x | +
- invisible(self)+ code = character(0), |
|
250 |
- }+ script = character(0), |
||
251 |
- ),+ metadata = list(type = "rds", file = file), |
||
252 |
- ## __Private Fields ====+ ...) { |
||
253 | -+ | 4x |
- private = list(+ dot_args <- list(...) |
254 | -+ | 4x |
- .keys = list(),+ checkmate::assert_list(dot_args, min.len = 0, names = "unique") |
255 | -+ | 4x |
- parents = list(),+ checkmate::assert_string(file) |
256 | -+ | 4x |
- join_pair = function(join_key) {+ if (!file.exists(file)) { |
257 | -527x | +1x |
- dataset_1 <- join_key$dataset_1+ stop("File ", file, " does not exist.", call. = FALSE) |
258 | -527x | +
- dataset_2 <- join_key$dataset_2+ } |
|
259 | -527x | +
- keys <- join_key$keys+ |
|
260 | -+ | 3x |
-
+ x_fun <- callable_function(readRDS) # nolint |
261 | -527x | +3x |
- if (is.null(private$.keys[[dataset_1]])) {+ args <- c(list(file = file), dot_args) |
262 | -283x | +3x |
- private$.keys[[dataset_1]] <- list()+ x_fun$set_args(args) |
263 |
- }+ |
||
264 | -527x | +3x |
- private$.keys[[dataset_1]][[dataset_2]] <- keys+ x <- dataset_connector( |
265 | -+ | 3x |
-
+ dataname = dataname, |
266 | -527x | +3x |
- if (dataset_2 != dataset_1) {+ pull_callable = x_fun, |
267 | -348x | +3x |
- if (is.null(private$.keys[[dataset_2]])) {+ keys = keys, |
268 | -199x | +3x |
- private$.keys[[dataset_2]] <- list()+ label = label, |
269 | -+ | 3x |
- }+ code = code_from_script(code, script), |
270 | -+ | 3x |
-
+ metadata = metadata |
271 | -348x | +
- if (length(keys) > 0) {+ ) |
|
272 | -343x | +
- keys <- setNames(names(keys), keys)+ |
|
273 | -+ | 3x |
- }+ return(x) |
274 | -348x | +
- private$.keys[[dataset_2]][[dataset_1]] <- keys+ } |
|
275 |
- }+ |
||
276 |
- },+ #' `RDS` `CDSICTealDatasetConnector` |
||
277 |
- # helper function to deterimine if two key sets contain incompatible keys+ #' |
||
278 |
- # return TRUE if compatible, throw error otherwise+ #' `r lifecycle::badge("stable")` |
||
279 |
- check_compatible_keys = function(join_key_1, join_key_2) {+ #' |
||
280 | -502x | +
- error_message <- function(dataset_1, dataset_2) {+ #' Create a `CDSICTealDatasetConnector` from `RDS` file with keys automatically |
|
281 | -7x | +
- stop(+ #' assigned by `dataname` |
|
282 | -7x | +
- paste("cannot specify multiple different join keys between datasets:", dataset_1, "and", dataset_2)+ #' |
|
283 |
- )+ #' @inheritParams rds_dataset_connector |
||
284 |
- }+ #' @inheritParams cdisc_dataset_connector |
||
285 |
-
+ #' |
||
286 |
-
+ #' @rdname rds_dataset_connector |
||
287 |
- # if first datasets and the second datasets match and keys+ #' |
||
288 |
- # must contain the same named elements+ #' @export |
||
289 | -502x | +
- if (join_key_1$dataset_1 == join_key_2$dataset_1 && join_key_1$dataset_2 == join_key_2$dataset_2) {+ rds_cdisc_dataset_connector <- function(dataname, |
|
290 | -305x | +
- if (!identical(sort(join_key_1$keys), sort(join_key_2$keys))) {+ file, |
|
291 | -3x | +
- error_message(join_key_1$dataset_1, join_key_1$dataset_2)+ keys = get_cdisc_keys(dataname), |
|
292 |
- }+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
||
293 |
- }+ label = character(0), |
||
294 |
-
+ code = character(0), |
||
295 |
- # if first dataset of join_key_1 matches second dataset of join_key_2+ script = character(0), |
||
296 |
- # and the first dataset of join_key_2 must match second dataset of join_key_1+ metadata = list(type = "rds", file = file), |
||
297 |
- # and keys must contain the same elements but with names and values swapped+ ...) { |
||
298 | -498x | +2x |
- if (join_key_1$dataset_1 == join_key_2$dataset_2 && join_key_1$dataset_2 == join_key_2$dataset_1) {+ x <- rds_dataset_connector( |
299 | -+ | 2x |
- # have to handle empty case differently as names(character(0)) is NULL+ dataname = dataname, |
300 | -92x | +2x |
- if (length(join_key_1$keys) == 0 && length(join_key_2$keys) == 0) {+ file = file, |
301 | 2x |
- return(TRUE)+ keys = keys, |
|
302 | -+ | 2x |
- }+ code = code_from_script(code, script), |
303 | -+ | 2x |
-
+ label = label, |
304 | -+ | 2x |
- if (+ metadata = metadata, |
305 | -90x | +
- xor(length(join_key_1$keys) == 0, length(join_key_2$keys) == 0) ||+ ... |
|
306 | -90x | +
- !identical(sort(join_key_1$keys), sort(setNames(names(join_key_2$keys), join_key_2$keys)))+ ) |
|
307 |
- ) {+ |
||
308 | -4x | +1x |
- error_message(join_key_1$dataset_1, join_key_1$dataset_2)+ res <- as_cdisc( |
309 | -+ | 1x |
- }+ x, |
310 | -+ | 1x |
- }+ parent = parent |
311 |
-
+ ) |
||
312 |
- # otherwise they are compatible+ |
||
313 | -492x | +1x |
- return(TRUE)+ return(res) |
314 |
- },+ } |
||
315 |
- # checks the parent child relations are valid+ |
||
316 |
- check_parent_child = function() {+ |
||
317 | -24x | +
- if (!is.null(self$get_parents())) {+ # SCRIPT ==== |
|
318 | -24x | +
- parents <- self$get_parents()+ #' Script `TealDatasetConnector` |
|
319 | -24x | +
- for (idx1 in seq_along(parents)) {+ #' |
|
320 | -46x | +
- name_from <- names(parents)[[idx1]]+ #' `r lifecycle::badge("stable")` |
|
321 | -46x | +
- for (idx2 in seq_along(parents[[idx1]])) {+ #' |
|
322 | -21x | +
- name_to <- parents[[idx1]][[idx2]]+ #' Create a `TealDatasetConnector` from `.R` file. |
|
323 | -21x | +
- keys_from <- self$get(name_from, name_to)+ #' |
|
324 | -21x | +
- keys_to <- self$get(name_to, name_from)+ #' @inheritParams dataset_connector |
|
325 | -21x | +
- if (length(keys_from) == 0 && length(keys_to) == 0) {+ #' @inheritParams fun_dataset_connector |
|
326 | -1x | +
- stop(sprintf("No join keys from %s to its parent (%s) and vice versa", name_from, name_to))+ #' @param file (`character`)\cr |
|
327 |
- }+ #' file location containing code to be evaluated in connector. Object obtained in the last |
||
328 | -20x | +
- if (length(keys_from) == 0) {+ #' call from file will be returned to the connector - same as `source(file = file)$value` |
|
329 | -! | +
- stop(sprintf("No join keys from %s to its parent (%s)", name_from, name_to))+ #' |
|
330 |
- }+ #' @export |
||
331 | -20x | +
- if (length(keys_to) == 0) {+ #' |
|
332 | -! | +
- stop(sprintf("No join keys from %s parent name (%s) to %s", name_from, name_to, name_from))+ #' @rdname script_dataset_connector |
|
333 |
- }+ #' |
||
334 |
- }+ #' @examples |
||
335 |
- }+ #' \dontrun{ |
||
336 |
- }+ #' x <- script_dataset_connector( |
||
337 |
- }+ #' dataname = "ADSL", |
||
338 |
- )+ #' file = "path/to/script.R", |
||
339 |
- )+ #' keys = get_cdisc_keys("ADSL") |
||
340 |
-
+ #' ) |
||
341 |
- # constructors ====+ #' x$get_code() |
||
342 |
-
+ #' } |
||
343 |
- #' Create a `JoinKeys` out of a list of `JoinKeySet` objects+ script_dataset_connector <- function(dataname, |
||
344 |
- #'+ file, |
||
345 |
- #' @description `r lifecycle::badge("stable")`+ keys = character(0), |
||
346 |
- #'+ label = character(0), |
||
347 |
- #' @param ... optional, a `JoinKeySet` objects created using the `join_key` function.+ code = character(0), |
||
348 |
- #' @details Note that join keys are symmetric although the relationship only needs+ script = character(0), |
||
349 |
- #' to be specified once.+ metadata = NULL, |
||
350 |
- #'+ ...) { |
||
351 | -+ | 4x |
- #' @return `JoinKeys`+ vars <- list(...) |
352 | -+ | 4x |
- #'+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
353 | -+ | 4x |
- #' @export+ checkmate::assert_string(file) |
354 | -+ | 4x |
- #'+ if (!file.exists(file)) { |
355 | -+ | 1x |
- #' @examples+ stop("File ", file, " does not exist.", call. = FALSE) |
356 |
- #' join_keys()+ } |
||
357 |
- #' join_keys(+ |
||
358 | -+ | 3x |
- #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")),+ x_fun <- callable_function(source) # nolint |
359 | -+ | 3x |
- #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y"))+ x_fun$set_args(list(file = file, local = TRUE)) |
360 |
- #' )+ |
||
361 | -+ | 3x |
- #' join_keys(+ x <- dataset_connector( |
362 | -+ | 3x |
- #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a"))+ dataname = dataname, |
363 | -+ | 3x |
- #' )+ pull_callable = x_fun, |
364 | -+ | 3x |
- join_keys <- function(...) {+ keys = keys, |
365 | -242x | +3x |
- x <- list(...)+ label = label, |
366 | -242x | +3x |
- res <- JoinKeys$new()+ code = code_from_script(code, script), |
367 | -242x | +3x |
- if (length(x) > 0) {+ vars = vars, |
368 | -109x | +3x |
- res$set(x)+ metadata = metadata |
369 |
- }+ ) |
||
371 | -234x | +3x |
- res+ return(x) |
374 |
- #' @title Getter for JoinKeys that returns the relationship between pairs of datasets+ #' Script `CDISCTealDatasetConnector` |
||
375 |
- #' @export+ #' |
||
376 |
- `[.JoinKeys` <- function(x, dataset_1, dataset_2 = NULL) {+ #' `r lifecycle::badge("stable")` |
||
377 | -23x | +
- checkmate::assert_string(dataset_1)+ #' |
|
378 | -23x | +
- checkmate::assert_string(dataset_2, null.ok = TRUE)+ #' Create a `CDISCTealDatasetConnector` from `script` file with keys assigned |
|
379 |
-
+ #' automatically by `dataname`. |
||
380 | -23x | +
- dataset_2 <- dataset_2 %||% dataset_1+ #' |
|
381 | -23x | +
- x$get(dataset_1, dataset_2)+ #' @inheritParams script_dataset_connector |
|
382 |
- }+ #' @inheritParams cdisc_dataset_connector |
||
383 |
-
+ #' |
||
384 |
- #' @rdname sub-.JoinKeys+ #' @rdname script_dataset_connector |
||
385 |
- #' @export+ #' |
||
386 |
- `[<-.JoinKeys` <- function(x, dataset_1, dataset_2 = NULL, value) {+ #' @export |
||
387 | -4x | +
- checkmate::assert_string(dataset_1)+ script_cdisc_dataset_connector <- function(dataname, |
|
388 | -4x | +
- checkmate::assert_string(dataset_2, null.ok = TRUE)+ file, |
|
389 |
-
+ keys = get_cdisc_keys(dataname), |
||
390 | -4x | +
- dataset_2 <- dataset_2 %||% dataset_1+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
|
391 | -4x | +
- x$mutate(dataset_1, dataset_2, value)+ label = character(0), |
|
392 | -4x | +
- x+ code = character(0), |
|
393 |
- }+ script = character(0), |
||
394 |
-
+ metadata = NULL, |
||
395 |
- #' @rdname join_keys+ ...) { |
||
396 | -+ | 1x |
- #' @details+ x <- script_dataset_connector( |
397 | -+ | 1x |
- #' `cdisc_join_keys` treat non-`JoinKeySet` arguments as possible CDISC datasets.+ dataname = dataname, |
398 | -+ | 1x |
- #' The `dataname` is extrapolated from the name (or fallback to the value itself if+ file = file, |
399 | -+ | 1x |
- #' it's a `character(1)`).+ keys = keys, |
400 | -+ | 1x |
- #'+ code = code_from_script(code, script), |
401 | -+ | 1x |
- #' @export+ script = script, |
402 | -+ | 1x |
- #' @examples+ label = label, |
403 | -+ | 1x |
- #' cdisc_join_keys(join_key("dataset_A", "dataset_B", c("col_1" = "col_a")), "ADTTE")+ metadata = metadata, |
404 |
- #'+ ... |
||
405 |
- cdisc_join_keys <- function(...) {+ ) |
||
406 | -61x | +
- data_objects <- list(...)+ |
|
407 | -+ | 1x |
-
+ res <- as_cdisc( |
408 | -61x | +1x |
- data_objects_parsed <- lapply(seq_along(data_objects), function(ix) {+ x, |
409 | -79x | +1x |
- item <- data_objects[[ix]]+ parent = parent |
410 |
-
+ ) |
||
411 | -79x | +
- name <- names(data_objects)[ix]+ |
|
412 | -76x | +1x |
- if (is.null(name) || identical(trimws(name), "")) name <- item # fallback to value if names are not set+ return(res) |
413 |
-
+ } |
||
414 | -61x | +
- if (+ |
|
415 | -79x | +
- checkmate::test_r6(item) &&+ |
|
416 | -79x | +
- checkmate::test_multi_class(+ # CODE ==== |
|
417 | -79x | +
- item,+ #' Code `TealDatasetConnector` |
|
418 | -79x | +
- classes = c("TealDataConnector", "TealDataset", "TealDatasetConnector")+ #' |
|
419 |
- )+ #' `r lifecycle::badge("stable")` |
||
420 |
- ) {+ #' |
||
421 |
- # Code not refactored for these data types as they'll be deprecated soon+ #' Create a `TealDatasetConnector` from a string of code. |
||
422 |
- # see logic in function `deprecated_join_keys_extract` called under `cdisc_data`+ #' |
||
423 | -34x | +
- return(NULL)+ #' @inheritParams dataset_connector |
|
424 | -61x | +
- } else if (+ #' @inheritParams fun_dataset_connector |
|
425 | -45x | +
- checkmate::test_class(item, "JoinKeySet") ||+ #' |
|
426 | -45x | +
- !checkmate::test_string(name, min.chars = 1) ||+ #' @param code (`character`)\cr |
|
427 | -45x | +
- !name %in% names(default_cdisc_keys)+ #' String containing the code to produce the object. |
|
428 |
- ) {+ #' The code must end in a call to the object. |
||
429 | -! | +
- return(list(item))+ #' @param mutate_code (`character`)\cr |
|
430 |
- }+ #' String containing the code used to mutate the object |
||
431 |
-
+ #' after it is produced. |
||
432 |
- # Add primary key+ #' @param mutate_script (`character`)\cr |
||
433 | -45x | +
- result <- list(join_key(name, keys = get_cdisc_keys(name)))+ #' Alternatively to `mutate_code` - location of the file containing modification code. |
|
434 | -45x | +
- keys_list <- default_cdisc_keys[[name]]+ #' Can't be used simultaneously with `mutate_script`. |
|
435 |
-
+ #' |
||
436 | -45x | +
- if (is.null(keys_list) || is.null(keys_list$parent) || is.null(keys_list$foreign)) {+ #' @export |
|
437 | -5x | +
- return(result)+ #' |
|
438 |
- }+ #' @rdname code_dataset_connector |
||
439 |
- # Add JoinKey with parent dataset (if exists)+ #' |
||
440 | -40x | +
- append(result, list(join_key(name, keys_list$parent, keys = keys_list$foreign)))+ #' @examples |
|
441 |
- })+ #' x <- code_dataset_connector( |
||
442 |
-
+ #' dataname = "ADSL", |
||
443 | -61x | +
- data_objects_parsed <- do.call(c, data_objects_parsed)+ #' keys = get_cdisc_keys("ADSL"), |
|
444 |
-
+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\"); ADSL" |
||
445 | -61x | +
- do.call(join_keys, as.list(data_objects_parsed[!is.null(data_objects_parsed)]))+ #' ) |
|
446 |
- }+ #' |
||
447 |
-
+ #' x$get_code() |
||
448 |
- # wrappers ====+ #' |
||
449 |
- #' Mutate `JoinKeys` with a new values+ #' mutate_dataset(x, code = "ADSL$new_variable <- 1") |
||
450 |
- #'+ #' x$get_code() |
||
451 |
- #' @description `r lifecycle::badge("experimental")`+ #' |
||
452 |
- #' Mutate `JoinKeys` with a new values+ #' file_example <- tempfile(fileext = ".R") |
||
453 |
- #'+ #' writeLines( |
||
454 |
- #' @param x (`JoinKeys`) object to be modified+ #' text = c( |
||
455 |
- #' @param dataset_1 (`character`) one dataset name+ #' "seed <- 1; ADSL <- radsl(cached = TRUE, seed = seed)\nADSL" |
||
456 |
- #' @param dataset_2 (`character`) other dataset name+ #' ), |
||
457 |
- #' @param val (named `character`) column names used to join+ #' con = file_example |
||
458 |
- #'+ #' ) |
||
459 |
- #' @return modified `JoinKeys` object+ #' |
||
460 |
- #'+ #' y <- code_dataset_connector( |
||
461 |
- #' @export+ #' dataname = "ADSL", |
||
462 |
- mutate_join_keys <- function(x, dataset_1, dataset_2, val) {+ #' keys = get_cdisc_keys("ADSL"), |
||
463 | -! | +
- UseMethod("mutate_join_keys")+ #' code = paste0(readLines(file_example), collapse = "\n") |
|
464 |
- }+ #' ) |
||
465 |
-
+ code_dataset_connector <- function(dataname, |
||
466 |
- #' @rdname mutate_join_keys+ code, |
||
467 |
- #' @export+ keys = character(0), |
||
468 |
- #' @examples+ label = character(0), |
||
469 |
- #' # JoinKeys ----+ mutate_code = character(0), |
||
470 |
- #'+ mutate_script = character(0), |
||
471 |
- #' x <- join_keys(+ metadata = NULL, |
||
472 |
- #' join_key("dataset_A", "dataset_B", c("col_1" = "col_a")),+ ...) { |
||
473 | -+ | 6x |
- #' join_key("dataset_A", "dataset_C", c("col_2" = "col_x", "col_3" = "col_y"))+ vars <- list(...) |
474 | -+ | 6x |
- #' )+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
475 | -+ | 6x |
- #' x$get("dataset_A", "dataset_B")+ checkmate::assert_string(code) |
476 | -+ | 6x |
- #'+ checkmate::assert_character(label, max.len = 1, any.missing = FALSE) |
477 |
- #' mutate_join_keys(x, "dataset_A", "dataset_B", c("col_1" = "col_10"))+ |
||
478 | -+ | 6x |
- #' x$get("dataset_A", "dataset_B")+ call <- callable_code(code = code) |
479 |
- mutate_join_keys.JoinKeys <- function(x, dataset_1, dataset_2, val) {+ |
||
480 | -! | +6x |
- x$mutate(dataset_1, dataset_2, val)+ x <- dataset_connector( |
481 | -+ | 6x |
- }+ dataname = dataname, |
482 | -+ | 6x |
-
+ pull_callable = call, |
483 | -+ | 6x |
- #' @rdname mutate_join_keys+ keys = keys, |
484 | -+ | 6x |
- #' @export+ label = label, |
485 | -+ | 6x |
- #' @examples+ code = code_from_script(mutate_code, mutate_script), |
486 | -+ | 6x |
- #' # TealData ----+ vars = vars, |
487 | -+ | 6x |
- #'+ metadata = metadata |
488 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ ) |
||
489 |
- #' ADRS <- teal.data::example_cdisc_data("ADRS")+ |
||
490 | -+ | 6x |
- #'+ return(x) |
491 |
- #' x <- cdisc_data(+ } |
||
492 |
- #' cdisc_dataset("ADSL", ADSL),+ |
||
493 |
- #' cdisc_dataset("ADRS", ADRS)+ #' Code `CDISCTealDatasetConnector` |
||
494 |
- #' )+ #' |
||
495 |
- #' x$get_join_keys()$get("ADSL", "ADRS")+ #' `r lifecycle::badge("stable")` |
||
497 |
- #' mutate_join_keys(x, "ADSL", "ADRS", c("COLUMN1" = "COLUMN2"))+ #' Create a `CDISCTealDatasetConnector` from a string of code with keys |
||
498 |
- #' x$get_join_keys()$get("ADSL", "ADRS")+ #' assigned automatically by `dataname`. |
||
499 |
- mutate_join_keys.TealData <- function(x, dataset_1, dataset_2, val) { # nolint+ #' |
||
500 | -! | +
- x$mutate_join_keys(dataset_1, dataset_2, val)+ #' @inheritParams code_dataset_connector |
|
501 |
- }+ #' @inheritParams cdisc_dataset_connector |
||
502 |
-
+ #' |
||
503 |
- #' Create a relationship between a pair of datasets+ #' @rdname code_dataset_connector |
||
505 |
- #' @description `r lifecycle::badge("stable")`+ #' @export |
||
506 |
- #'+ code_cdisc_dataset_connector <- function(dataname, |
||
507 |
- #' @details `join_key()` will create a relationship for the variables on a pair+ code, |
||
508 |
- #' of datasets.+ keys = get_cdisc_keys(dataname), |
||
509 |
- #'+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
||
510 |
- #' @inheritParams mutate_join_keys+ label = character(0), |
||
511 |
- #' @param dataset_2 (`character`) other dataset name. In case it is omitted, then it+ mutate_code = character(0), |
||
512 |
- #' will create a primary key for `dataset_1`.+ metadata = NULL, |
||
513 |
- #' @param keys (optionally named `character`) where `names(keys)` are columns in `dataset_1`+ ...) { |
||
514 | -+ | 1x |
- #' with relationship to columns of `dataset_2` given by the elements in `keys`.+ x <- code_dataset_connector( |
515 | -+ | 1x |
- #' If `names(keys)` is `NULL` then the same column names are used for both `dataset_1`+ dataname = dataname, |
516 | -+ | 1x |
- #' and `dataset_2`.+ code = code, |
517 | -+ | 1x |
- #'+ keys = keys, |
518 | -+ | 1x |
- #' @return object of class `JoinKeySet` to be passed into `join_keys` function.+ mutate_code = mutate_code, |
519 | -+ | 1x |
- #'+ label = label, |
520 | -+ | 1x |
- #' @seealso [join_keys()]+ metadata = metadata, |
521 |
- #'+ ... |
||
522 |
- #' @export+ ) |
||
523 |
- join_key <- function(dataset_1, dataset_2 = NULL, keys) {+ |
||
524 | -557x | +1x |
- checkmate::assert_string(dataset_1)+ res <- as_cdisc( |
525 | -557x | +1x |
- checkmate::assert_string(dataset_2, null.ok = TRUE)+ x, |
526 | -554x | +1x |
- checkmate::assert_character(keys, any.missing = FALSE)+ parent = parent |
527 |
-
+ ) |
||
528 | -552x | +
- dataset_2 <- dataset_2 %||% dataset_1+ |
|
529 | -+ | 1x |
-
+ return(res) |
530 | -552x | +
- if (length(keys) > 0) {+ } |
|
531 | -508x | +
- if (is.null(names(keys))) {+ |
|
532 | -219x | +
- names(keys) <- keys+ # CSV ==== |
|
533 |
- }+ #' `csv` `TealDatasetConnector` |
||
534 |
-
+ #' |
||
535 | -508x | +
- if (any(names(keys) == "")) {+ #' `r lifecycle::badge("stable")` |
|
536 | -4x | +
- names(keys)[names(keys) == "" & keys != ""] <- keys[names(keys) == "" & keys != ""]+ #' |
|
537 |
- }+ #' Create a `TealDatasetConnector` from `csv` (or general delimited file). |
||
538 |
-
+ #' |
||
539 | -508x | +
- stopifnot(!is.null(names(keys)))+ #' |
|
540 | -508x | +
- stopifnot(!anyDuplicated(keys))+ #' @inheritParams dataset_connector |
|
541 | -507x | +
- stopifnot(!anyDuplicated(names(keys)))+ #' @inheritParams fun_dataset_connector |
|
542 |
- }+ #' |
||
543 |
-
+ #' @param file (`character`)\cr |
||
544 | -550x | +
- if (dataset_1 == dataset_2 && any(names(keys) != keys)) {+ #' path to (`.csv)` (or general delimited) file that contains `data.frame` object |
|
545 | -2x | +
- stop("Keys within a dataset must match exactly: keys = c('A' = 'B') are not allowed")+ #' |
|
546 |
- }+ #' @param ... (`optional`)\cr |
||
547 |
-
+ #' additional arguments applied to pull function (`readr::read_delim`) by default |
||
548 | -548x | +
- structure(+ #' `delim = ","`. |
|
549 | -548x | +
- list(+ #' |
|
550 | -548x | +
- dataset_1 = dataset_1,+ #' @export |
|
551 | -548x | +
- dataset_2 = dataset_2,+ #' |
|
552 | -548x | +
- keys = keys+ #' @rdname csv_dataset_connector |
|
553 |
- ),+ #' |
||
554 | -548x | +
- class = "JoinKeySet"+ #' @examples |
|
555 |
- )+ #' \dontrun{ |
||
556 |
- }+ #' x <- csv_dataset_connector( |
1 | +557 |
- ## TealDataset ====+ #' dataname = "ADSL", |
||
2 | +558 |
- #'+ #' file = "path/to/file.csv", |
||
3 | +559 |
- #'+ #' delim = ",", |
||
4 | +560 |
- #' @title R6 Class representing a dataset with its attributes+ #' col_types = quote(readr::cols(AGE = "i")) |
||
5 | +561 |
- #'+ #' ) |
||
6 | +562 |
- #' @description `r lifecycle::badge("stable")`+ #' x$get_code() |
||
7 | +563 |
- #' Any `data.frame` object can be stored inside this object.+ #' } |
||
8 | +564 |
- #' Some attributes like colnames, dimension or column names for a specific type will+ csv_dataset_connector <- function(dataname, |
||
9 | +565 |
- #' be automatically derived.+ file, |
||
10 | +566 |
- #'+ keys = character(0), |
||
11 | +567 |
- #' @param dataname (`character`)\cr+ label = character(0), |
||
12 | +568 |
- #' A given name for the dataset it may not contain spaces+ code = character(0), |
||
13 | +569 |
- #' @param x (`data.frame`)\cr+ script = character(0), |
||
14 | +570 |
- #' @param keys optional, (`character`)\cr+ metadata = list(type = "csv", file = file), |
||
15 | +571 |
- #' Vector with primary keys+ ...) { |
||
16 | -+ | |||
572 | +13x |
- #' @param code (`character`)\cr+ dot_args <- list(...) |
||
17 | -+ | |||
573 | +13x |
- #' A character string defining the code needed to produce the data set in `x`.+ checkmate::assert_list(dot_args, min.len = 0, names = "unique") |
||
18 | +574 |
- #' `initialize()` and `recreate()` accept code as `CodeClass`+ |
||
19 | -+ | |||
575 | +13x |
- #' which is also needed to preserve the code uniqueness and correct order.+ check_pkg_quietly( |
||
20 | -+ | |||
576 | +13x |
- #' @param label (`character`)\cr+ "readr", |
||
21 | -+ | |||
577 | +13x |
- #' Label to describe the dataset+ "library readr is required to use csv connectors please install it." |
||
22 | +578 |
- #' @param vars (named `list`)) \cr+ ) |
||
23 | +579 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ |
||
24 | +580 |
- #' other constant value, this/these object(s) should be included as named+ # add default delim as "," |
||
25 | -+ | |||
581 | +13x |
- #' element(s) of the list. For example if this object code needs `ADSL`+ if (!"delim" %in% names(dot_args)) { |
||
26 | -+ | |||
582 | +6x |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ dot_args$delim <- "," |
||
27 | +583 |
- #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to+ } |
||
28 | +584 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ |
||
29 | -+ | |||
585 | +13x |
- #' are included to this object as local `vars` and they cannot be modified+ checkmate::assert_string(file) |
||
30 | -+ | |||
586 | +10x |
- #' within another dataset.+ if (!file.exists(file)) { |
||
31 | -+ | |||
587 | +1x |
- #' @param metadata (named `list` or `NULL`) \cr+ stop("File ", file, " does not exist.", call. = FALSE) |
||
32 | +588 |
- #' Field containing metadata about the dataset. Each element of the list+ } |
||
33 | +589 |
- #' should be atomic and of length one.+ |
||
34 | -+ | |||
590 | +9x |
- #'+ x_fun <- callable_function("readr::read_delim") # using read_delim as preserves dates (read.csv does not) |
||
35 | -+ | |||
591 | +9x |
- #' @seealso [`MAETealDataset`]+ args <- c(list(file = file), dot_args) |
||
36 | -+ | |||
592 | +9x |
- #'+ x_fun$set_args(args) |
||
37 | +593 |
- TealDataset <- R6::R6Class( # nolint+ + |
+ ||
594 | +9x | +
+ x <- dataset_connector(+ |
+ ||
595 | +9x | +
+ dataname = dataname,+ |
+ ||
596 | +9x | +
+ pull_callable = x_fun,+ |
+ ||
597 | +9x | +
+ keys = keys,+ |
+ ||
598 | +9x | +
+ label = label,+ |
+ ||
599 | +9x | +
+ code = code_from_script(code, script),+ |
+ ||
600 | +9x | +
+ metadata = metadata |
||
38 | +601 |
- "TealDataset",+ ) |
||
39 | +602 | |||
603 | +9x | +
+ return(x)+ |
+ ||
40 | +604 |
- ## __Public Methods ====+ } |
||
41 | +605 |
- public = list(+ |
||
42 | +606 |
- #' @description+ #' `csv` `CDISCTealDatasetConnector` |
||
43 | +607 |
- #' Create a new object of `TealDataset` class+ #' |
||
44 | +608 |
- initialize = function(dataname,+ #' `r lifecycle::badge("stable")` |
||
45 | +609 |
- x,+ #' |
||
46 | +610 |
- keys = character(0),+ #' Create a `CDISCTealDatasetConnector` from `csv` (or general delimited) file |
||
47 | +611 |
- code = character(0),+ #' with keys and parent name assigned automatically by `dataname`. |
||
48 | +612 |
- label = character(0),+ #' |
||
49 | +613 |
- vars = list(),+ #' @inheritParams csv_dataset_connector |
||
50 | +614 |
- metadata = NULL) {+ #' @inheritParams cdisc_dataset_connector |
||
51 | -490x | +|||
615 | +
- checkmate::assert_string(dataname)+ #' |
|||
52 | -490x | +|||
616 | +
- checkmate::assert_data_frame(x)+ #' @rdname csv_dataset_connector |
|||
53 | -490x | +|||
617 | +
- checkmate::assert_character(keys, any.missing = FALSE)+ #' |
|||
54 | -490x | +|||
618 | +
- checkmate::assert(+ #' @export |
|||
55 | -490x | +|||
619 | +
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ csv_cdisc_dataset_connector <- function(dataname, |
|||
56 | -490x | +|||
620 | +
- checkmate::check_class(code, "CodeClass")+ file, |
|||
57 | +621 |
- )+ keys = get_cdisc_keys(dataname), |
||
58 | +622 |
- # label might be NULL also because of taking label attribute from data.frame - missing attr is NULL+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
||
59 | -490x | +|||
623 | +
- checkmate::assert_character(label, max.len = 1, null.ok = TRUE, any.missing = FALSE)+ label = character(0), |
|||
60 | -490x | +|||
624 | +
- checkmate::assert_list(vars, names = "named")+ code = character(0), |
|||
61 | +625 |
-
+ script = character(0), |
||
62 | -490x | +|||
626 | +
- validate_metadata(metadata)+ metadata = list(type = "csv", file = file), |
|||
63 | +627 |
-
+ ...) { |
||
64 | -487x | +628 | +9x |
- private$.raw_data <- x+ x <- csv_dataset_connector( |
65 | -487x | +629 | +9x |
- private$metadata <- metadata+ dataname = dataname, |
66 | -+ | |||
630 | +9x |
-
+ file = file, |
||
67 | -487x | +631 | +9x |
- private$set_dataname(dataname)+ keys = keys, |
68 | -487x | +632 | +9x |
- self$set_vars(vars)+ code = code_from_script(code, script), |
69 | -487x | +633 | +9x |
- self$set_dataset_label(label)+ label = label, |
70 | -487x | +634 | +9x |
- self$set_keys(keys)+ metadata = metadata, |
71 | +635 |
-
+ ... |
||
72 | +636 |
- # needed if recreating dataset - we need to preserve code order and uniqueness+ ) |
||
73 | -487x | +|||
637 | +
- private$code <- CodeClass$new()+ |
|||
74 | -487x | +638 | +9x |
- if (is.character(code)) {+ res <- as_cdisc( |
75 | -281x | -
- self$set_code(code)- |
- ||
76 | -+ | 639 | +9x |
- } else {+ x, |
77 | -206x | +640 | +9x |
- private$code$append(code)+ parent = parent |
78 | +641 |
- }+ ) |
||
79 | +642 | |||
80 | -487x | -
- logger::log_trace("TealDataset initialized for dataset: { deparse1(self$get_dataname()) }.")- |
- ||
81 | -487x | +643 | +9x |
- return(invisible(self))+ return(res) |
82 | +644 |
- },+ } |
||
83 | +645 | |||
84 | +646 |
- #' @description+ # FUN ==== |
||
85 | +647 |
- #' Recreate this `TealDataset` with its current attributes.+ #' Function Dataset Connector |
||
86 | +648 |
- #'+ #' |
||
87 | +649 |
- #' @return a new object of the `TealDataset` class+ #' `r lifecycle::badge("stable")` |
||
88 | +650 |
- recreate = function(dataname = self$get_dataname(),+ #' |
||
89 | +651 |
- x = self$get_raw_data(),+ #' Create a `TealDatasetConnector` from `function` and its arguments. |
||
90 | +652 |
- keys = self$get_keys(),+ #' |
||
91 | +653 |
- code = private$code,+ #' @inheritParams dataset_connector |
||
92 | +654 |
- label = self$get_dataset_label(),+ #' |
||
93 | +655 |
- vars = list(),+ #' @param fun (`function`)\cr |
||
94 | +656 |
- metadata = self$get_metadata()) {- |
- ||
95 | -53x | -
- res <- self$initialize(+ #' a custom function to obtain dataset. |
||
96 | -53x | +|||
657 | +
- dataname = dataname,+ #' @param fun_args (`list`)\cr |
|||
97 | -53x | +|||
658 | +
- x = x,+ #' additional arguments for (`func`). |
|||
98 | -53x | +|||
659 | +
- keys = keys,+ #' @param func_name (`name`)\cr |
|||
99 | -53x | +|||
660 | +
- code = code,+ #' for internal purposes, please keep it default |
|||
100 | -53x | +|||
661 | +
- label = label,+ #' @param ... Additional arguments applied to pull function. |
|||
101 | -53x | +|||
662 | +
- vars = vars,+ #' In case when this object code depends on the `raw_data` from the other |
|||
102 | -53x | +|||
663 | +
- metadata = metadata+ #' `TealDataset`, `TealDatasetConnector` object(s) or other constant value, |
|||
103 | +664 |
- )+ #' this/these object(s) should be included. Please note that `vars` |
||
104 | -53x | +|||
665 | +
- logger::log_trace("TealDataset$recreate recreated dataset: { deparse1(self$get_dataname()) }.")+ #' are included to this object as local `vars` and they cannot be modified |
|||
105 | -53x | +|||
666 | +
- return(res)+ #' within another dataset. |
|||
106 | +667 |
- },+ #' @export |
||
107 | +668 |
- #' @description+ #' |
||
108 | +669 |
- #' Prints this `TealDataset`.+ #' @rdname fun_dataset_connector |
||
109 | +670 |
- #'+ #' |
||
110 | +671 |
- #' @param ... additional arguments to the printing method+ #' @examples |
||
111 | +672 |
- #' @return invisibly self+ #' my_data <- function(...) { |
||
112 | +673 |
- print = function(...) {- |
- ||
113 | -8x | -
- check_ellipsis(...)- |
- ||
114 | -8x | -
- cat(sprintf(+ #' data.frame( |
||
115 | -8x | +|||
674 | +
- "A %s object containing the following data.frame (%s rows and %s columns):\n",+ #' ID = paste0("ABC_", seq_len(10)), |
|||
116 | -8x | +|||
675 | +
- class(self)[1],+ #' var1 = rnorm(n = 10), |
|||
117 | -8x | +|||
676 | +
- self$get_nrow(),+ #' var2 = rnorm(n = 10), |
|||
118 | -8x | +|||
677 | +
- self$get_ncol()+ #' var3 = rnorm(n = 10) |
|||
119 | +678 |
- ))+ #' ) |
||
120 | -8x | +|||
679 | +
- print(head(as.data.frame(self$get_raw_data())))+ #' } |
|||
121 | -8x | +|||
680 | +
- if (self$get_nrow() > 6) {+ #' y <- fun_dataset_connector( |
|||
122 | -1x | +|||
681 | +
- cat("...\n")+ #' dataname = "XYZ", |
|||
123 | +682 |
- }+ #' fun = my_data |
||
124 | -8x | +|||
683 | +
- invisible(self)+ #' ) |
|||
125 | +684 |
- },+ #' |
||
126 | +685 |
- # ___ getters ====+ #' y$get_code() |
||
127 | +686 |
- #' @description+ #' |
||
128 | +687 |
- #' Performs any delayed mutate calls before returning self.+ #' y$pull() |
||
129 | +688 |
- #'+ #' |
||
130 | +689 |
- #' @return dataset (`TealDataset`)+ #' get_raw_data(y) |
||
131 | +690 |
- get_dataset = function() {+ fun_dataset_connector <- function(dataname, |
||
132 | -228x | +|||
691 | +
- if (self$is_mutate_delayed() && !private$is_any_dependency_delayed()) {+ fun, |
|||
133 | -2x | +|||
692 | +
- private$mutate_eager()+ fun_args = NULL, |
|||
134 | +693 |
- }+ keys = character(0), |
||
135 | -228x | +|||
694 | +
- return(self)+ label = character(0), |
|||
136 | +695 |
- },+ code = character(0), |
||
137 | +696 |
- #' @description+ script = character(0), |
||
138 | +697 |
- #' Get all dataset attributes+ func_name = substitute(fun), |
||
139 | +698 |
- #' @return (named `list`) with dataset attributes+ metadata = NULL, |
||
140 | +699 |
- get_attrs = function() {+ ...) { |
||
141 | -! | +|||
700 | +7x |
- x <- append(+ vars <- list(...) |
||
142 | -! | +|||
701 | +7x |
- attributes(self$get_raw_data()),+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
143 | -! | +|||
702 | +
- list(+ |
|||
144 | -! | +|||
703 | +7x |
- column_labels = self$get_column_labels(),+ stopifnot(is.function(fun)) |
||
145 | -! | +|||
704 | +
- row_labels = self$get_row_labels(),+ |
|||
146 | -! | +|||
705 | +7x |
- dataname = self$get_dataname(),+ stopifnot(is.list(fun_args) || is.null(fun_args)) |
||
147 | -! | +|||
706 | +
- dataset_label = self$get_dataset_label(),+ |
|||
148 | -! | +|||
707 | +7x |
- keys = self$get_keys()+ cal <- if (!is.symbol(func_name)) as.call(func_name) else NULL |
||
149 | +708 |
- )+ |
||
150 | -+ | |||
709 | +7x |
- )+ is_pak <- FALSE |
||
151 | -! | +|||
710 | +7x |
- return(x)+ is_locked <- TRUE |
||
152 | -+ | |||
711 | +7x |
- },+ if ((!is.null(cal)) && identical(cal[[1]], as.symbol("::"))) { |
||
153 | -+ | |||
712 | +5x |
- #' @description+ pak <- cal[[2]] |
||
154 | -+ | |||
713 | +5x |
- #' Derive the raw data frame inside this object+ pak_char <- as.character(pak) # nolint |
||
155 | -+ | |||
714 | +5x |
- #' @return `data.frame`+ library(pak_char, character.only = TRUE) |
||
156 | -+ | |||
715 | +5x |
- get_raw_data = function() {+ func_name <- cal[[3]] |
||
157 | -356x | +716 | +5x |
- private$.raw_data+ is_pak <- TRUE |
158 | -+ | |||
717 | +5x |
- },+ is_locked <- TRUE |
||
159 | +718 |
- #' @description+ } else { |
||
160 | -+ | |||
719 | +2x |
- #' Derive the names of all `numeric` columns+ is_locked <- environmentIsLocked(environment(fun)) |
||
161 | +720 |
- #' @return `character` vector.+ } |
||
162 | +721 |
- get_numeric_colnames = function() {+ |
||
163 | -1x | +722 | +7x |
- private$get_class_colnames("numeric")+ func_char <- as.character(func_name) |
164 | +723 |
- },+ |
||
165 | -+ | |||
724 | +7x |
- #' @description+ ee <- new.env(parent = parent.env(globalenv())) |
||
166 | +725 |
- #' Derive the names of all `character` columns+ |
||
167 | -+ | |||
726 | +7x |
- #' @return `character` vector.+ ee$library <- function(...) { |
||
168 | -+ | |||
727 | +! |
- get_character_colnames = function() {+ mc <- match.call() |
||
169 | -1x | +|||
728 | +! |
- private$get_class_colnames("character")+ mc[[1]] <- quote(base::library) |
||
170 | -+ | |||
729 | +! |
- },+ eval(mc, envir = globalenv()) |
||
171 | -+ | |||
730 | +! |
- #' @description+ this_env <- parent.frame() |
||
172 | -+ | |||
731 | +! |
- #' Derive the names of all `factor` columns+ if (!identical(this_env, globalenv())) { |
||
173 | -+ | |||
732 | +! |
- #' @return `character` vector.+ parent.env(this_env) <- parent.env(globalenv()) |
||
174 | +733 |
- get_factor_colnames = function() {- |
- ||
175 | -1x | -
- private$get_class_colnames("factor")+ } |
||
176 | +734 |
- },+ } |
||
177 | +735 |
- #' @description+ |
||
178 | +736 |
- #' Derive the column names+ |
||
179 | -+ | |||
737 | +7x |
- #' @return `character` vector.+ if (!is_pak && !is_locked) { |
||
180 | -+ | |||
738 | +2x |
- get_colnames = function() {+ eval(bquote(.(func_name) <- get(.(func_char), .(environment(fun)))), envir = ee) |
||
181 | -128x | +739 | +2x |
- colnames(private$.raw_data)+ eval(bquote(.(func_name) <- rlang::set_env(.(func_name), .(ee))), envir = ee) |
182 | +740 |
- },+ } |
||
183 | +741 |
- #' @description+ |
||
184 | -+ | |||
742 | +7x |
- #' Derive the column labels+ x_fun <- CallableFunction$new(fun, env = ee) |
||
185 | -+ | |||
743 | +7x |
- #' @return `character` vector.+ x_fun$set_args(fun_args) |
||
186 | +744 |
- get_column_labels = function() {+ |
||
187 | -1x | +745 | +7x |
- col_labels(private$.raw_data, fill = FALSE)+ vars[[func_char]] <- ee[[func_char]] |
188 | +746 |
- },+ |
||
189 | -+ | |||
747 | +7x |
- #' @description+ x <- dataset_connector( |
||
190 | -+ | |||
748 | +7x |
- #' Get the number of columns of the data+ dataname = dataname, |
||
191 | -+ | |||
749 | +7x |
- #' @return `numeric` vector+ pull_callable = x_fun, |
||
192 | -+ | |||
750 | +7x |
- get_ncol = function() {+ keys = keys, |
||
193 | -9x | +751 | +7x |
- ncol(private$.raw_data)+ code = code_from_script(code, script), |
194 | -+ | |||
752 | +7x |
- },+ label = label, |
||
195 | -+ | |||
753 | +7x |
- #' @description+ vars = vars, |
||
196 | -+ | |||
754 | +7x |
- #' Get the number of rows of the data+ metadata = metadata |
||
197 | +755 |
- #' @return `numeric` vector+ ) |
||
198 | +756 |
- get_nrow = function() {+ |
||
199 | -17x | +757 | +7x |
- nrow(private$.raw_data)+ return(x) |
200 | +758 |
- },+ } |
||
201 | +759 |
- #' @description+ |
||
202 | +760 |
- #' Derive the row names+ #' Function `CDISCTealDatasetConnector` |
||
203 | +761 |
- #' @return `character` vector.+ #' |
||
204 | +762 |
- get_rownames = function() {+ #' `r lifecycle::badge("stable")` |
||
205 | -2x | +|||
763 | +
- rownames(private$.raw_data)+ #' |
|||
206 | +764 |
- },+ #' Create a `CDISCTealDatasetConnector` from `function` and its arguments |
||
207 | +765 |
- #' @description+ #' with keys and parent name assigned automatically by `dataname`. |
||
208 | +766 |
- #' Derive the row labels+ #' |
||
209 | +767 |
- #' @return `character` vector.+ #' @inheritParams fun_dataset_connector |
||
210 | +768 |
- get_row_labels = function() {+ #' @inheritParams cdisc_dataset_connector |
||
211 | -1x | +|||
769 | +
- c()+ #' |
|||
212 | +770 |
- },+ #' @rdname fun_dataset_connector |
||
213 | +771 |
- #' @description+ #' |
||
214 | +772 |
- #' Derive the `name` which was formerly called `dataname`+ #' @export |
||
215 | +773 |
- #' @return `character` name of the dataset+ fun_cdisc_dataset_connector <- function(dataname, |
||
216 | +774 |
- get_dataname = function() {+ fun, |
||
217 | -1257x | +|||
775 | +
- private$dataname+ fun_args = NULL, |
|||
218 | +776 |
- },+ keys = get_cdisc_keys(dataname), |
||
219 | +777 |
- #' @description+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
||
220 | +778 |
- #' Derive the `dataname`+ label = character(0), |
||
221 | +779 |
- #' @return `character` name of the dataset+ code = character(0), |
||
222 | +780 |
- get_datanames = function() {+ script = character(0), |
||
223 | -159x | +|||
781 | +
- private$dataname+ func_name = substitute(fun), |
|||
224 | +782 |
- },+ metadata = NULL, |
||
225 | +783 |
- #' @description+ ...) { |
||
226 | -+ | |||
784 | +4x |
- #' Derive the `label` which was former called `datalabel`+ x <- fun_dataset_connector( |
||
227 | -+ | |||
785 | +4x |
- #' @return `character` label of the dataset+ dataname = dataname, |
||
228 | -+ | |||
786 | +4x |
- get_dataset_label = function() {+ fun = fun, |
||
229 | -93x | +787 | +4x |
- private$dataset_label+ fun_args = fun_args, |
230 | -+ | |||
788 | +4x |
- },+ func_name = func_name, |
||
231 | -+ | |||
789 | +4x |
- #' @description+ keys = keys, |
||
232 | -+ | |||
790 | +4x |
- #' Get primary keys of dataset+ label = label, |
||
233 | -+ | |||
791 | +4x |
- #' @return (`character` vector) with dataset primary keys+ code = code, |
||
234 | -+ | |||
792 | +4x |
- get_keys = function() {+ script = script, |
||
235 | -208x | +793 | +4x |
- private$.keys+ metadata = metadata, |
236 | +794 |
- },+ ... |
||
237 | +795 |
- #' @description+ ) |
||
238 | +796 |
- #' Get metadata of dataset+ |
||
239 | -+ | |||
797 | +4x |
- #' @return (named `list`)+ res <- as_cdisc( |
||
240 | -+ | |||
798 | +4x |
- get_metadata = function() {+ x, |
||
241 | -100x | +799 | +4x |
- private$metadata+ parent = parent |
242 | +800 |
- },+ ) |
||
243 | +801 |
- #' @description+ + |
+ ||
802 | +4x | +
+ return(res) |
||
244 | +803 |
- #' Get the list of dependencies that are `TealDataset` or `TealDatasetConnector` objects+ } |
||
245 | +804 |
- #'+ |
||
246 | +805 |
- #' @return `list`+ |
||
247 | +806 |
- get_var_r6 = function() {+ # PYTHON ==== |
||
248 | -105x | +|||
807 | +
- return(private$var_r6)+ #' `Python` `TealDatasetConnector` |
|||
249 | +808 |
- },+ #' |
||
250 | +809 |
- # ___ setters ====+ #' `r lifecycle::badge("experimental")` |
||
251 | +810 |
- #' @description+ #' Create a `TealDatasetConnector` from `.py` file or through python code supplied directly. |
||
252 | +811 |
- #' Overwrites `TealDataset` or `TealDatasetConnector` dependencies of this `TealDataset` with+ #' |
||
253 | +812 |
- #' those found in `datasets`. Reassignment+ #' @details |
||
254 | +813 |
- #' refers only to the provided `datasets`, other `vars` remains the same.+ #' Note that in addition to the `reticulate` package, support for python requires an |
||
255 | +814 |
- #' @details+ #' existing python installation. By default, `reticulate` will attempt to use the |
||
256 | +815 |
- #' Reassign `vars` in this object to keep references up to date after deep clone.+ #' location `Sys.which("python")`, however the path to the python installation can be |
||
257 | +816 |
- #' Update is done based on the objects passed in `datasets` argument.+ #' supplied directly via `reticulate::use_python`. |
||
258 | +817 |
- #' Overwrites dependencies with names matching the names of the objects passed+ #' |
||
259 | +818 |
- #' in `datasets`.+ #' The `teal` API for delayed data requires the python code or script to return a |
||
260 | +819 |
- #' @param datasets (`named list` of `TealDataset(s)` or `TealDatasetConnector(s)`)\cr+ #' data.frame object. For this, the `pandas` package is required. This can be installed |
||
261 | +820 |
- #' objects with valid pointers.+ #' using `reticulate::py_install("pandas")`. |
||
262 | +821 |
- #' @return NULL invisible+ #' |
||
263 | +822 |
- #' @examples+ #' Please see the package documentation for more details. |
||
264 | +823 |
- #' test_dataset <- teal.data:::TealDataset$new(+ #' |
||
265 | +824 |
- #' dataname = "iris",+ #' @inheritParams dataset_connector |
||
266 | +825 |
- #' x = iris,+ #' @inheritParams code_dataset_connector |
||
267 | +826 |
- #' vars = list(dep = teal.data:::TealDataset$new("iris2", iris))+ #' @param file (`character`)\cr |
||
268 | +827 |
- #' )+ #' Path to the file location containing the python script used to generate the object. |
||
269 | +828 |
- #' test_dataset$reassign_datasets_vars(+ #' @param code (`character`)\cr |
||
270 | +829 |
- #' list(iris2 = teal.data:::TealDataset$new("iris2", head(iris)))+ #' string containing the python code to be run using `reticulate`. Carefully consider |
||
271 | +830 |
- #' )+ #' indentation to follow proper python syntax. |
||
272 | +831 |
- #'+ #' @param object (`character`)\cr |
||
273 | +832 |
- reassign_datasets_vars = function(datasets) {+ #' name of the object from the python script that is assigned to the dataset to be used. |
||
274 | -7x | +|||
833 | +
- checkmate::assert_list(datasets, min.len = 0, names = "unique")+ #' |
|||
275 | +834 |
-
+ #' @note |
||
276 | -7x | +|||
835 | +
- common_var_r6 <- intersect(names(datasets), names(private$var_r6))+ #' Raises an error when passed `code` and `file` are passed at the same time. |
|||
277 | -7x | +|||
836 | +
- private$var_r6[common_var_r6] <- datasets[common_var_r6]+ #' |
|||
278 | +837 |
-
+ #' When using `code`, keep in mind that when using `reticulate` with delayed data, python |
||
279 | -7x | +|||
838 | +
- common_vars <- intersect(names(datasets), names(private$vars))+ #' functions do not have access to other objects in the `code` and must be self contained. |
|||
280 | -7x | +|||
839 | +
- private$vars[common_vars] <- datasets[common_vars]+ #' In the following example, the function `makedata()` doesn't have access to variable `x`: |
|||
281 | +840 |
-
+ #' |
||
282 | -7x | +|||
841 | +
- common_mutate_vars <- intersect(names(datasets), names(private$mutate_vars))+ #' \preformatted{import pandas as pd |
|||
283 | -7x | +|||
842 | +
- private$mutate_vars[common_mutate_vars] <- datasets[common_mutate_vars]+ #' |
|||
284 | +843 |
-
+ #' x = 1 |
||
285 | -7x | -
- logger::log_trace(- |
- ||
286 | -7x | +|||
844 | +
- "TealDataset$reassign_datasets_vars reassigned vars for dataset: { deparse1(self$get_dataname()) }."+ #' def makedata(): |
|||
287 | +845 |
- )+ #' return pd.DataFrame({'x': [x, 2], 'y': [3, 4]}) |
||
288 | -7x | +|||
846 | +
- invisible(NULL)+ #' |
|||
289 | +847 |
- },+ #' data = makedata()} |
||
290 | +848 |
- #' @description+ #' |
||
291 | +849 |
- #' Set the label for the dataset+ #' When using custom functions, the function environment must be entirely self contained: |
||
292 | +850 |
- #' @return (`self`) invisibly for chaining+ #' |
||
293 | +851 |
- set_dataset_label = function(label) {+ #' \preformatted{def makedata(): |
||
294 | -506x | +|||
852 | +
- if (is.null(label)) {+ #' import pandas as pd |
|||
295 | -166x | +|||
853 | +
- label <- character(0)+ #' x = 1 |
|||
296 | +854 |
- }+ #' return pd.DataFrame({'x': [x, 2], 'y': [3, 4]}) |
||
297 | -506x | +|||
855 | +
- checkmate::assert_character(label, max.len = 1, any.missing = FALSE)+ #' |
|||
298 | -506x | +|||
856 | +
- private$dataset_label <- label+ #' data = makedata() |
|||
299 | +857 |
-
+ #' } |
||
300 | -506x | +|||
858 | +
- logger::log_trace(+ #' |
|||
301 | -506x | +|||
859 | +
- "TealDataset$set_dataset_label dataset_label set for dataset: { deparse1(self$get_dataname()) }."+ #' **Additional `reticulate` considerations:** |
|||
302 | +860 |
- )+ #' 1. Note that when using pull `vars`, `R` objects referenced in the python |
||
303 | -506x | +|||
861 | +
- return(invisible(self))+ #' code or script have to be prefixed with `r.`. |
|||
304 | +862 |
- },+ #' 2. `reticulate` isn't able to convert `POSIXct` objects. Please take extra |
||
305 | +863 |
- #' @description+ #' care when working with `datetime` variables. |
||
306 | +864 |
- #' Set new keys+ #' |
||
307 | +865 |
- #' @return (`self`) invisibly for chaining.+ #' Please read the official documentation for the `reticulate` package for additional |
||
308 | +866 |
- set_keys = function(keys) {+ #' features and current limitations. |
||
309 | -625x | +|||
867 | +
- checkmate::assert_character(keys, any.missing = FALSE)+ #' |
|||
310 | -625x | +|||
868 | +
- private$.keys <- keys+ #' @export |
|||
311 | -625x | +|||
869 | +
- logger::log_trace(sprintf(+ #' |
|||
312 | -625x | +|||
870 | +
- "TealDataset$set_keys set the keys %s for dataset: %s",+ #' @rdname python_dataset_connector |
|||
313 | -625x | +|||
871 | +
- paste(keys, collapse = ", "),+ #' |
|||
314 | -625x | +|||
872 | +
- self$get_dataname()+ #' @examples |
|||
315 | +873 |
- ))+ #' \dontrun{ |
||
316 | -625x | +|||
874 | +
- return(invisible(self))+ #' library(reticulate) |
|||
317 | +875 |
- },+ #' |
||
318 | +876 |
-
+ #' # supply python code directly in R |
||
319 | +877 |
- #' @description+ #' |
||
320 | +878 |
- #' Adds variables which code depends on+ #' x <- python_dataset_connector( |
||
321 | +879 |
- #'+ #' "ADSL", |
||
322 | +880 |
- #' @param vars (`named list`) contains any R object which code depends on+ #' code = "import pandas as pd |
||
323 | +881 |
- #' @return (`self`) invisibly for chaining+ #' data = pd.DataFrame({'STUDYID': [1, 2], 'USUBJID': [3, 4]})", |
||
324 | +882 |
- set_vars = function(vars) {+ #' object = "data" |
||
325 | -576x | +|||
883 | +
- private$set_vars_internal(vars, is_mutate_vars = FALSE)+ #' ) |
|||
326 | -572x | +|||
884 | +
- logger::log_trace("TealDataset$set_vars vars set for dataset: { deparse1(self$get_dataname()) }.")+ #' |
|||
327 | +885 |
-
+ #' x$pull() |
||
328 | -572x | +|||
886 | +
- return(invisible(NULL))+ #' x$get_raw_data() |
|||
329 | +887 |
- },+ #' |
||
330 | +888 |
- #' @description+ #' # supply an external python script |
||
331 | +889 |
- #' Sets reproducible code+ #' |
||
332 | +890 |
- #'+ #' python_file <- tempfile(fileext = ".py") |
||
333 | +891 |
- #' @return (`self`) invisibly for chaining+ #' writeLines( |
||
334 | +892 |
- set_code = function(code) {+ #' text = "import pandas as pd |
||
335 | -300x | +|||
893 | +
- checkmate::assert_character(code, max.len = 1, any.missing = FALSE)+ #' data = pd.DataFrame({'STUDYID': [1, 2], 'USUBJID': [3, 4]})", |
|||
336 | -300x | +|||
894 | +
- if (length(code) > 0 && code != "") {+ #' con = python_file |
|||
337 | -120x | +|||
895 | +
- private$code$set_code(+ #' ) |
|||
338 | -120x | +|||
896 | +
- code = code,+ #' |
|||
339 | -120x | +|||
897 | +
- dataname = self$get_datanames(),+ #' x <- python_dataset_connector( |
|||
340 | -120x | +|||
898 | +
- deps = names(private$vars)+ #' "ADSL", |
|||
341 | +899 |
- )+ #' file = python_file, |
||
342 | +900 |
- }+ #' object = "data", |
||
343 | -300x | +|||
901 | +
- logger::log_trace("TealDataset$set_code code set for dataset: { deparse1(self$get_dataname()) }.")+ #' ) |
|||
344 | -300x | +|||
902 | +
- return(invisible(NULL))+ #' |
|||
345 | +903 |
- },+ #' x$pull() |
||
346 | +904 |
-
+ #' x$get_raw_data() |
||
347 | +905 |
- # ___ get_code ====+ #' |
||
348 | +906 |
- #' @description+ #' # supply pull `vars` from R |
||
349 | +907 |
- #' Get code to get data+ #' |
||
350 | +908 |
- #'+ #' y <- 8 |
||
351 | +909 |
- #' @param deparse (`logical`) whether return deparsed form of a call+ #' x <- python_dataset_connector( |
||
352 | +910 |
- #'+ #' "ADSL", |
||
353 | +911 |
- #' @return optionally deparsed `call` object+ #' code = "import pandas as pd |
||
354 | +912 |
- get_code = function(deparse = TRUE) {+ #' data = pd.DataFrame({'STUDYID': [r.y], 'USUBJID': [r.y]})", |
||
355 | -60x | +|||
913 | +
- checkmate::assert_flag(deparse)+ #' object = "data", |
|||
356 | -60x | +|||
914 | +
- res <- self$get_code_class()$get_code(deparse = deparse)+ #' vars = list(y = y) |
|||
357 | -60x | +|||
915 | +
- return(res)+ #' ) |
|||
358 | +916 |
- },+ #' |
||
359 | +917 |
- #' @description+ #' x$pull() |
||
360 | +918 |
- #' Get internal `CodeClass` object+ #' x$get_raw_data() |
||
361 | +919 |
- #' @param nodeps (`logical(1)`) whether `CodeClass` should not contain the code+ #' } |
||
362 | +920 |
- #' of the dependent `vars`+ python_dataset_connector <- function(dataname, |
||
363 | +921 |
- #' the `mutate`+ file, |
||
364 | +922 |
- #' @return `CodeClass`+ code, |
||
365 | +923 |
- get_code_class = function(nodeps = FALSE) {+ object = dataname, |
||
366 | -383x | +|||
924 | +
- res <- CodeClass$new()+ keys = character(0), |
|||
367 | +925 |
- # precise order matters+ label = character(0), |
||
368 | -383x | +|||
926 | +
- if (!nodeps) {+ mutate_code = character(0), |
|||
369 | -370x | +|||
927 | +
- res$append(list_to_code_class(private$vars))+ mutate_script = character(0), |
|||
370 | -370x | +|||
928 | +
- res$append(list_to_code_class(private$mutate_vars))+ vars = list(), |
|||
371 | +929 |
- }+ metadata = NULL) { |
||
372 | -383x | +|||
930 | +! |
- res$append(private$code)+ if (!requireNamespace("reticulate", quietly = TRUE)) { |
||
373 | -383x | +|||
931 | +! |
- res$append(private$mutate_list_to_code_class())+ stop("Cannot load package 'reticulate' - please install the package.", call. = FALSE) |
||
374 | +932 |
-
+ } |
||
375 | -383x | +|||
933 | +! |
- return(res)+ if (utils::packageVersion("reticulate") < 1.22) { |
||
376 | -+ | |||
934 | +! |
- },+ stop("Please upgrade package 'reticulate', teal.data requires version >= 1.22") |
||
377 | +935 |
- #' @description+ } |
||
378 | +936 |
- #' Get internal `CodeClass` object+ |
||
379 | -+ | |||
937 | +! |
- #'+ checkmate::assert_string(object) |
||
380 | -+ | |||
938 | +! |
- #' @return `CodeClass`+ if (!xor(missing(code), missing(file))) stop("Exactly one of 'code' and 'script' is required") |
||
381 | +939 |
- get_mutate_code_class = function() {+ |
||
382 | +940 | ! |
- res <- CodeClass$new()+ if (!missing(file)) { |
|
383 | +941 | ! |
- res$append(list_to_code_class(private$mutate_vars))+ checkmate::assert_string(file) |
|
384 | +942 | ! |
- res$append(private$mutate_list_to_code_class())+ checkmate::assert_file_exists(file, extension = "py") |
|
385 | -+ | |||
943 | +! |
-
+ x_fun <- CallablePythonCode$new("py_run_file") # nolint |
||
386 | +944 | ! |
- return(res)+ x_fun$set_args(list(file = file, local = TRUE)) |
|
387 | +945 |
- },+ } else { |
||
388 | -+ | |||
946 | +! |
- #' @description+ checkmate::assert_string(code) |
||
389 | -+ | |||
947 | +! |
- #' Get internal `vars` object+ x_fun <- CallablePythonCode$new("py_run_string") # nolint+ |
+ ||
948 | +! | +
+ x_fun$set_args(list(code = code, local = TRUE)) |
||
390 | +949 |
- #'+ } |
||
391 | +950 |
- #' @return `list`+ + |
+ ||
951 | +! | +
+ x_fun$set_object(object) |
||
392 | +952 |
- get_vars = function() {+ |
||
393 | -17x | +|||
953 | +! |
- return(c(+ x <- dataset_connector( |
||
394 | -17x | +|||
954 | +! |
- private$vars,+ dataname = dataname, |
||
395 | -17x | +|||
955 | +! |
- private$mutate_vars[!names(private$mutate_vars) %in% names(private$vars)]+ pull_callable = x_fun, |
||
396 | -+ | |||
956 | +! |
- ))+ keys = keys, |
||
397 | -+ | |||
957 | +! |
- },+ label = label, |
||
398 | -+ | |||
958 | +! |
- #' @description+ code = code_from_script(mutate_code, mutate_script), |
||
399 | -+ | |||
959 | +! |
- #' Get internal `mutate_vars` object+ vars = vars, |
||
400 | -+ | |||
960 | +! |
- #'+ metadata = metadata |
||
401 | +961 |
- #' @return `list`+ ) |
||
402 | +962 |
- get_mutate_vars = function() {+ |
||
403 | -2x | +|||
963 | +! |
- return(private$mutate_vars)+ return(x) |
||
404 | +964 |
- },+ } |
||
405 | +965 | |||
406 | +966 |
- #' @description+ #' `Python` `CDISCTealDatasetConnector` |
||
407 | +967 |
- #' Whether mutate code has delayed evaluation.+ #' |
||
408 | +968 |
- #' @return `logical`+ #' `r lifecycle::badge("experimental")` |
||
409 | +969 |
- is_mutate_delayed = function() {+ #' Create a `CDISCTealDatasetConnector` from `.py` file or through python code supplied directly. |
||
410 | -348x | +|||
970 | +
- return(length(private$mutate_code) > 0)+ #' |
|||
411 | +971 |
- },+ #' @inheritParams python_dataset_connector |
||
412 | +972 |
-
+ #' @inheritParams cdisc_dataset_connector |
||
413 | +973 |
- # ___ mutate ====+ #' |
||
414 | +974 |
- #' @description+ #' @export |
||
415 | +975 |
- #' Mutate dataset by code+ #' |
||
416 | +976 |
- #'+ #' @rdname python_dataset_connector |
||
417 | +977 |
- #' @param code (`CodeClass`) or (`character`) R expressions to be executed+ python_cdisc_dataset_connector <- function(dataname, |
||
418 | +978 |
- #' @param vars a named list of R objects that `code` depends on to execute+ file, |
||
419 | +979 |
- #' @param force_delay (`logical`) used by the containing `TealDatasetConnector` object+ code, |
||
420 | +980 |
- #'+ object = dataname, |
||
421 | +981 |
- #' Either code or script must be provided, but not both.+ keys = get_cdisc_keys(dataname), |
||
422 | +982 |
- #'+ parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"), |
||
423 | +983 |
- #' @return (`self`) invisibly for chaining+ mutate_code = character(0), |
||
424 | +984 |
- mutate = function(code, vars = list(), force_delay = FALSE) {+ mutate_script = character(0), |
||
425 | -98x | +|||
985 | +
- logger::log_trace(+ label = character(0), |
|||
426 | -98x | +|||
986 | +
- sprintf(+ vars = list(), |
|||
427 | -98x | +|||
987 | +
- "TealDatasetConnector$mutate mutating dataset '%s' using the code (%s lines) and vars (%s).",+ metadata = NULL) { |
|||
428 | -98x | +|||
988 | +! |
- self$get_dataname(),+ x <- python_dataset_connector( |
||
429 | -98x | +|||
989 | +! |
- length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)),+ dataname = dataname, |
||
430 | -98x | +|||
990 | +! |
- paste(names(vars), collapse = ", ")+ file = file, |
||
431 | -+ | |||
991 | +! |
- )+ code = code, |
||
432 | -+ | |||
992 | +! |
- )+ object = object, |
||
433 | -+ | |||
993 | +! |
-
+ keys = keys, |
||
434 | -98x | +|||
994 | +! |
- checkmate::assert_flag(force_delay)+ mutate_code = mutate_code, |
||
435 | -98x | +|||
995 | +! |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ mutate_script = mutate_script, |
||
436 | -98x | +|||
996 | +! |
- checkmate::assert(+ label = label, |
||
437 | -98x | +|||
997 | +! |
- checkmate::check_string(code),+ vars = vars, |
||
438 | -98x | +|||
998 | +! |
- checkmate::check_class(code, "CodeClass")+ metadata = metadata |
||
439 | +999 |
- )+ ) |
||
440 | +1000 | |||
441 | -97x | +|||
1001 | +! |
- if (inherits(code, "PythonCodeClass")) {+ res <- as_cdisc( |
||
442 | +1002 | ! |
- self$set_vars(vars)+ x, |
|
443 | +1003 | ! |
- self$set_code(code$get_code())+ parent = parent |
|
444 | -! | +|||
1004 | +
- new_df <- code$eval(dataname = self$get_dataname())+ ) |
|||
445 | +1005 | |||
1006 | +! | +
+ return(res)+ |
+ ||
446 | +1007 |
- # dataset is recreated by replacing data by mutated object+ } |
447 | +1 |
- # mutation code is added to the code which replicates the data+ ## TealDataConnection ==== |
||
448 | -! | +|||
2 | +
- self$recreate(+ #' @title A `TealDataConnection` class of objects |
|||
449 | -! | +|||
3 | +
- x = new_df,+ #' @description `r lifecycle::badge("stable")` |
|||
450 | -! | +|||
4 | +
- vars = list()+ #' |
|||
451 | +5 |
- )+ #' Objects of this class store the connection to a data source. |
||
452 | +6 |
- } else {+ #' It can be a database or server connection. |
||
453 | -97x | +|||
7 | +
- private$mutate_delayed(code, vars)+ #' |
|||
454 | -93x | +|||
8 | +
- if (!(private$is_any_dependency_delayed(vars) || force_delay)) {+ #' @examples |
|||
455 | -58x | +|||
9 | +
- private$mutate_eager()+ #' open_fun <- callable_function(data.frame) # define opening function |
|||
456 | +10 |
- }+ #' open_fun$set_args(list(x = 1:5)) # define fixed arguments to opening function |
||
457 | +11 |
- }+ #' |
||
458 | -88x | +|||
12 | +
- logger::log_trace(+ #' close_fun <- callable_function(sum) # define closing function |
|||
459 | -88x | +|||
13 | +
- sprintf(+ #' close_fun$set_args(list(x = 1:5)) # define fixed arguments to closing function |
|||
460 | -88x | +|||
14 | +
- "TealDataset$mutate mutated dataset '%s' using the code (%s lines) and vars (%s).",+ #' |
|||
461 | -88x | +|||
15 | +
- self$get_dataname(),+ #' ping_fun <- callable_function(function() TRUE) |
|||
462 | -88x | +|||
16 | +
- length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)),+ #' |
|||
463 | -88x | +|||
17 | +
- paste(names(vars), collapse = ", ")+ #' x <- data_connection( # define connection |
|||
464 | +18 |
- )+ #' ping_fun = ping_fun, # define ping function |
||
465 | +19 |
- )+ #' open_fun = open_fun, # define opening function |
||
466 | +20 |
-
+ #' close_fun = close_fun # define closing function |
||
467 | -88x | +|||
21 | +
- return(invisible(self))+ #' ) |
|||
468 | +22 |
- },+ #' |
||
469 | +23 |
-
+ #' x$set_open_args(args = list(y = letters[1:5])) # define additional arguments if necessary |
||
470 | +24 |
- # ___ check ====+ #' |
||
471 | +25 |
- #' @description+ #' x$open() # call opening function |
||
472 | +26 |
- #' Check to determine if the raw data is reproducible from the `get_code()` code.+ #' x$get_open_call() # check reproducible R code |
||
473 | +27 |
- #' @return+ #' |
||
474 | +28 |
- #' `TRUE` if the dataset generated from evaluating the+ #' # get data from connection via TealDataConnector$get_dataset() |
||
475 | +29 |
- #' `get_code()` code is identical to the raw data, else `FALSE`.+ #' \dontrun{ |
||
476 | +30 |
- check = function() {+ #' x$open(args = list(x = 1:5, y = letters[1:5])) # able to call opening function with arguments |
||
477 | -23x | +|||
31 | +
- logger::log_trace(+ #' x$close() # call closing function |
|||
478 | -23x | +|||
32 | +
- "TealDataset$check executing the code to reproduce dataset: { deparse1(self$get_dataname()) }..."+ #' } |
|||
479 | +33 |
- )+ #' |
||
480 | -23x | +|||
34 | +
- if (!checkmate::test_character(self$get_code(), len = 1, pattern = "\\w+")) {+ TealDataConnection <- R6::R6Class( # nolint |
|||
481 | -2x | +|||
35 | +
- stop(+ ## __Public Methods ==== |
|||
482 | -2x | +|||
36 | +
- sprintf(+ "TealDataConnection", |
|||
483 | -2x | +|||
37 | +
- "Cannot check preprocessing code of '%s' - code is empty.",+ public = list( |
|||
484 | -2x | +|||
38 | +
- self$get_dataname()+ #' @description |
|||
485 | +39 |
- )+ #' Create a new `TealDataConnection` object |
||
486 | +40 |
- )+ #' |
||
487 | +41 |
- }+ #' @param open_fun (`CallableFunction`) function to open connection |
||
488 | +42 |
-
+ #' @param close_fun (`CallableFunction`) function to close connection |
||
489 | -21x | +|||
43 | +
- new_set <- private$execute_code(+ #' @param ping_fun (`CallableFunction`) function to ping connection |
|||
490 | -21x | +|||
44 | +
- code = self$get_code_class(),+ #' @param if_conn_obj optional, (`logical`) whether to store `conn` object returned from opening |
|||
491 | -21x | +|||
45 | +
- vars = c(+ #' connection |
|||
492 | -21x | +|||
46 | +
- list(), # list() in the beginning to ensure c.list+ #' @return new `TealDataConnection` object+ |
+ |||
47 | ++ |
+ initialize = function(open_fun = NULL, close_fun = NULL, ping_fun = NULL, if_conn_obj = FALSE) { |
||
493 | -21x | +48 | +29x |
- private$vars,+ checkmate::assert_flag(if_conn_obj) |
494 | -21x | +49 | +29x |
- setNames(list(self), self$get_dataname())+ if (!is.null(open_fun)) { |
495 | -+ | |||
50 | +21x |
- )+ stopifnot(inherits(open_fun, "Callable")) |
||
496 | -+ | |||
51 | +21x |
- )+ private$set_open_fun(open_fun) |
||
497 | +52 |
-
+ } |
||
498 | -21x | +53 | +29x |
- res_check <- tryCatch(+ if (!is.null(close_fun)) { |
499 | -+ | |||
54 | +3x |
- {+ stopifnot(inherits(close_fun, "Callable")) |
||
500 | -21x | +55 | +3x |
- identical(self$get_raw_data(), new_set)+ private$set_close_fun(close_fun) |
501 | +56 |
- },+ } |
||
502 | -21x | +57 | +29x |
- error = function(e) {+ if (!is.null(ping_fun)) { |
503 | +58 | ! |
- FALSE+ stopifnot(inherits(ping_fun, "Callable")) |
|
504 | -+ | |||
59 | +! |
- }+ private$set_ping_fun(ping_fun) |
||
505 | +60 |
- )+ } |
||
506 | -21x | +61 | +29x |
- logger::log_trace("TealDataset$check { deparse1(self$get_dataname()) } reproducibility result: { res_check }.")+ private$if_conn_obj <- if_conn_obj |
507 | +62 | |||
508 | -21x | -
- return(res_check)- |
- ||
509 | -- |
- },- |
- ||
510 | -+ | 63 | +29x |
- #' @description+ private$open_ui <- function(id) { |
511 | -+ | |||
64 | +! |
- #' Check if keys has been specified correctly for dataset. Set of `keys`+ NULL |
||
512 | +65 |
- #' should distinguish unique rows or be `character(0)`.+ } |
||
513 | -+ | |||
66 | +29x |
- #'+ private$ping_ui <- function(id) { |
||
514 | -+ | |||
67 | +! |
- #' @return `TRUE` if dataset has been already pulled, else `FALSE`+ NULL |
||
515 | +68 |
- check_keys = function(keys = private$.keys) {- |
- ||
516 | -78x | -
- if (length(keys) > 0) {+ } |
||
517 | -46x | +69 | +29x |
- if (!all(keys %in% self$get_colnames())) {+ private$close_ui <- function(id) { |
518 | -2x | +|||
70 | +! |
- stop("Primary keys specifed for ", self$get_dataname(), " do not exist in the data.")+ NULL |
||
519 | +71 |
- }+ } |
||
520 | +72 | |||
521 | -44x | +73 | +29x |
- duplicates <- get_key_duplicates(self$get_raw_data(), keys)+ logger::log_trace( |
522 | -44x | +74 | +29x |
- if (nrow(duplicates) > 0) {+ sprintf( |
523 | -1x | +75 | +29x |
- stop(+ "TealDataConnection initialized with:%s%s%s%s.", |
524 | -1x | +76 | +29x |
- "Duplicate primary key values found in the dataset '", self$get_dataname(), "' :\n",+ if (!is.null(open_fun)) " open_fun" else "", |
525 | -1x | +77 | +29x |
- paste0(utils::capture.output(print(duplicates))[-c(1, 3)], collapse = "\n"),+ if (!is.null(close_fun)) " close_fun" else "", |
526 | -1x | +78 | +29x |
- call. = FALSE+ if (!is.null(ping_fun)) " ping_fun" else "", |
527 | -+ | |||
79 | +29x |
- )+ if (if_conn_obj) " conn" else "" |
||
528 | +80 |
- }+ ) |
||
529 | +81 |
- }+ ) |
||
530 | -75x | +82 | +29x |
- logger::log_trace("TealDataset$check_keys keys checking passed for dataset: { deparse1(self$get_dataname()) }.")+ invisible(self) |
531 | +83 |
}, |
||
532 | +84 |
#' @description |
||
533 | +85 |
- #' Check if dataset has already been pulled.+ #' Finalize method closing the connection. |
||
534 | +86 |
#' |
||
535 | +87 |
- #' @return `TRUE` if dataset has been already pulled, else `FALSE`+ #' @return NULL |
||
536 | +88 |
- is_pulled = function() {+ finalize = function() { |
||
537 | -174x | +89 | +29x |
- return(TRUE)+ self$close(silent = TRUE, try = TRUE) |
538 | -+ | |||
90 | +29x |
- }+ NULL |
||
539 | +91 |
- ),+ }, |
||
540 | +92 |
- ## __Private Fields ====+ #' @description |
||
541 | +93 |
- private = list(+ #' If connection is opened |
||
542 | +94 |
- .raw_data = data.frame(),+ #' |
||
543 | +95 |
- metadata = NULL,+ #' If open connection has been successfully evaluated |
||
544 | +96 |
- dataname = character(0),+ #' |
||
545 | +97 |
- code = NULL, # CodeClass after initialization+ #' @return (`logical`) if connection is open |
||
546 | +98 |
- vars = list(),+ is_opened = function() { |
||
547 | -+ | |||
99 | +4x |
- var_r6 = list(),+ return(private$opened) |
||
548 | +100 |
- dataset_label = character(0),+ }, |
||
549 | +101 |
- .keys = character(0),+ #' @description |
||
550 | -- |
- mutate_code = list(),- |
- ||
551 | -- |
- mutate_vars = list(),- |
- ||
552 | -- | - - | -||
553 | -- |
- ## __Private Methods ====- |
- ||
554 | -- |
- mutate_delayed = function(code, vars) {- |
- ||
555 | -97x | -
- private$set_vars_internal(vars, is_mutate_vars = TRUE)- |
- ||
556 | -93x | -
- private$mutate_code[[length(private$mutate_code) + 1]] <- list(code = code, deps = names(vars))- |
- ||
557 | -93x | -
- logger::log_trace(- |
- ||
558 | -93x | -
- sprintf(- |
- ||
559 | -93x | -
- "TealDatasetConnector$mutate_delayed set the code (%s lines) and vars (%s) for dataset: %s.",- |
- ||
560 | -93x | -
- length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code, keep.source = FALSE)),- |
- ||
561 | -93x | -
- paste(names(vars), collapse = ", "),- |
- ||
562 | -93x | -
- self$get_dataname()- |
- ||
563 | -- |
- )- |
- ||
564 | -- |
- )- |
- ||
565 | -93x | -
- return(invisible(self))- |
- ||
566 | -- |
- },- |
- ||
567 | -- |
- mutate_eager = function() {- |
- ||
568 | -60x | -
- logger::log_trace(- |
- ||
569 | -60x | -
- "TealDatasetConnector$mutate_eager executing mutate code for dataset: { deparse1(self$get_dataname()) }..."- |
- ||
570 | -- |
- )- |
- ||
571 | -60x | -
- new_df <- private$execute_code(- |
- ||
572 | -60x | -
- code = private$mutate_list_to_code_class(),- |
- ||
573 | -60x | -
- vars = c(- |
- ||
574 | -60x | -
- list(), # list() in the beginning to ensure c.list- |
- ||
575 | -60x | -
- private$vars,- |
- ||
576 | -- |
- # if they have the same name, then they are guaranteed to be identical objects.- |
- ||
577 | -60x | -
- private$mutate_vars[!names(private$mutate_vars) %in% names(private$vars)],- |
- ||
578 | -60x | -
- setNames(list(self), self$get_dataname())- |
- ||
579 | -- |
- )- |
- ||
580 | -- |
- )- |
- ||
581 | -- | - - | -||
582 | -- |
- # code set after successful evaluation- |
- ||
583 | -- |
- # otherwise code != dataset- |
- ||
584 | -- |
- # private$code$append(private$mutate_code) # nolint- |
- ||
585 | -55x | -
- private$append_mutate_code()- |
- ||
586 | -55x | -
- self$set_vars(private$mutate_vars)- |
- ||
587 | -55x | -
- private$mutate_code <- list()- |
- ||
588 | -55x | -
- private$mutate_vars <- list()- |
- ||
589 | -- | - - | -||
590 | -- |
- # dataset is recreated by replacing data by mutated object- |
- ||
591 | -- |
- # mutation code is added to the code which replicates the data- |
- ||
592 | -- |
- # because new_code contains also code of the- |
- ||
593 | -55x | -
- new_self <- self$recreate(- |
- ||
594 | -55x | -
- x = new_df,- |
- ||
595 | -55x | -
- vars = list()- |
- ||
596 | -- |
- )- |
- ||
597 | -- | - - | -||
598 | -55x | -
- logger::log_trace(- |
- ||
599 | -55x | -
- "TealDatasetConnector$mutate_eager executed mutate code for dataset: { deparse1(self$get_dataname()) }."- |
- ||
600 | -- |
- )- |
- ||
601 | -- | - - | -||
602 | -55x | -
- new_self- |
- ||
603 | -- |
- },- |
- ||
604 | -- | - - | -||
605 | -- |
- # need to have a custom deep_clone because one of the key fields are reference-type object- |
- ||
606 | -- |
- # in particular: code is a R6 object that wouldn't be cloned using default clone(deep = T)- |
- ||
607 | -- |
- deep_clone = function(name, value) {- |
- ||
608 | -1044x | -
- deep_clone_r6(name, value)- |
- ||
609 | -- |
- },- |
- ||
610 | -- |
- get_class_colnames = function(class_type = "character") {- |
- ||
611 | -3x | -
- checkmate::assert_string(class_type)- |
- ||
612 | -3x | -
- return_cols <- self$get_colnames()[which(vapply(- |
- ||
613 | -3x | -
- lapply(self$get_raw_data(), class),- |
- ||
614 | -3x | -
- function(x, target_class_name) any(x %in% target_class_name),- |
- ||
615 | -3x | -
- logical(1),- |
- ||
616 | -3x | -
- target_class_name = class_type- |
- ||
617 | -- |
- ))]- |
- ||
618 | -- | - - | -||
619 | -3x | -
- return(return_cols)- |
- ||
620 | -- |
- },- |
- ||
621 | -- |
- mutate_list_to_code_class = function() {- |
- ||
622 | -443x | -
- res <- CodeClass$new()- |
- ||
623 | -443x | -
- for (mutate_code in private$mutate_code) {- |
- ||
624 | -121x | -
- if (inherits(mutate_code$code, "CodeClass")) {- |
- ||
625 | -14x | -
- res$append(mutate_code$code)- |
- ||
626 | -- |
- } else {- |
- ||
627 | -107x | -
- res$set_code(- |
- ||
628 | -107x | -
- code = mutate_code$code,- |
- ||
629 | -107x | -
- dataname = private$dataname,- |
- ||
630 | -107x | -
- deps = mutate_code$deps- |
- ||
631 | -- |
- )- |
- ||
632 | -- |
- }- |
- ||
633 | -- |
- }- |
- ||
634 | -443x | -
- return(res)- |
- ||
635 | -- |
- },- |
- ||
636 | -- |
- append_mutate_code = function() {- |
- ||
637 | -55x | -
- for (mutate_code in private$mutate_code) {- |
- ||
638 | -57x | -
- if (inherits(mutate_code$code, "CodeClass")) {- |
- ||
639 | -11x | -
- private$code$append(mutate_code$code)- |
- ||
640 | -- |
- } else {- |
- ||
641 | -46x | -
- private$code$set_code(- |
- ||
642 | -46x | -
- code = mutate_code$code,- |
- ||
643 | -46x | -
- dataname = private$dataname,- |
- ||
644 | -46x | -
- deps = mutate_code$deps- |
- ||
645 | -- |
- )- |
- ||
646 | -- |
- }- |
- ||
647 | -- |
- }- |
- ||
648 | -- |
- },- |
- ||
649 | +102 |
- is_any_dependency_delayed = function(vars = list()) {- |
- ||
650 | -101x | -
- any(vapply(- |
- ||
651 | -101x | -
- c(list(), private$var_r6, vars),- |
- ||
652 | -101x | -
- FUN.VALUE = logical(1),- |
- ||
653 | -101x | -
- FUN = function(var) {- |
- ||
654 | -130x | -
- if (inherits(var, "TealDatasetConnector")) {- |
- ||
655 | -68x | -
- !var$is_pulled() || var$is_mutate_delayed()- |
- ||
656 | -62x | -
- } else if (inherits(var, "TealDataset")) {- |
- ||
657 | -50x | -
- var$is_mutate_delayed()+ #' Check if connection has not failed. |
||
658 | +103 |
- } else {- |
- ||
659 | -12x | -
- FALSE+ #' |
||
660 | +104 |
- }+ #' @return (`logical`) `TRUE` if connection failed, else `FALSE` |
||
661 | +105 |
- }+ is_failed = function() { |
||
662 | -+ | |||
106 | +! |
- ))+ self$is_open_failed() || self$is_close_failed() |
||
663 | +107 |
}, |
||
664 | -- | - - | -||
665 | -- |
- # Set variables which code depends on- |
- ||
666 | -- |
- # @param vars (`named list`) contains any R object which code depends on- |
- ||
667 | +108 |
- # @param is_mutate_vars (`logical(1)`) whether this var is used in mutate code+ #' @description |
||
668 | +109 |
- set_vars_internal = function(vars, is_mutate_vars = FALSE) {- |
- ||
669 | -673x | -
- checkmate::assert_flag(is_mutate_vars)- |
- ||
670 | -673x | -
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' Run simple application that uses its `ui` and `server` fields to open the |
||
671 | +110 | - - | -||
672 | -673x | -
- total_vars <- c(list(), private$vars, private$mutate_vars)+ #' connection. |
||
673 | +111 | - - | -||
674 | -673x | -
- if (length(vars) > 0) {+ #' |
||
675 | +112 |
- # not allowing overriding variable names- |
- ||
676 | -89x | -
- over_rides <- names(vars)[vapply(- |
- ||
677 | -89x | -
- names(vars),- |
- ||
678 | -89x | -
- FUN.VALUE = logical(1),- |
- ||
679 | -89x | -
- FUN = function(var_name) {- |
- ||
680 | -92x | -
- var_name %in% names(total_vars) &&- |
- ||
681 | -92x | -
- !identical(total_vars[[var_name]], vars[[var_name]])+ #' Useful for debugging |
||
682 | +113 |
- }+ #' |
||
683 | +114 |
- )]- |
- ||
684 | -89x | -
- if (length(over_rides) > 0) {- |
- ||
685 | -2x | -
- stop(paste("Variable name(s) already used:", paste(over_rides, collapse = ", ")))+ #' @return An object that represents the app |
||
686 | +115 |
- }- |
- ||
687 | -87x | -
- if (is_mutate_vars) {- |
- ||
688 | -44x | -
- private$mutate_vars <- c(+ launch = function() { |
||
689 | -44x | +|||
116 | +! |
- private$mutate_vars[!names(private$mutate_vars) %in% names(vars)],+ shinyApp( |
||
690 | -44x | +|||
117 | +! |
- vars+ ui = fluidPage( |
||
691 | -+ | |||
118 | +! |
- )+ include_js_files(), |
||
692 | -+ | |||
119 | +! |
- } else {+ theme = get_teal_bs_theme(), |
||
693 | -43x | +|||
120 | +! |
- private$vars <- c(+ fluidRow( |
||
694 | -43x | +|||
121 | +! |
- private$vars[!names(private$vars) %in% names(vars)],+ column( |
||
695 | -43x | +|||
122 | +! |
- vars+ width = 8, |
||
696 | -+ | |||
123 | +! |
- )+ offset = 2, |
||
697 | -+ | |||
124 | +! |
- }+ tags$div( |
||
698 | -+ | |||
125 | +! |
- }+ id = "connection_inputs", |
||
699 | -+ | |||
126 | +! |
- # only adding dependencies if checks passed+ self$get_open_ui(id = "data_connection"), |
||
700 | -671x | +|||
127 | +! |
- private$set_var_r6(vars)+ actionButton("submit", "Submit"), |
||
701 | -665x | +|||
128 | +! |
- return(invisible(NULL))+ `data-proxy-click` = "submit" # handled by jscode in custom.js - hit enter to submit |
||
702 | +129 |
- },+ ), |
||
703 | -+ | |||
130 | +! |
-
+ shinyjs::hidden( |
||
704 | -+ | |||
131 | +! |
- # Evaluate script code to modify data or to reproduce data+ tags$div( |
||
705 | -+ | |||
132 | +! |
- #+ id = "connection_set", |
||
706 | -+ | |||
133 | +! |
- # Evaluate script code to modify data or to reproduce data+ div( |
||
707 | -+ | |||
134 | +! |
- # @param vars (named `list`) additional pre-requisite vars to execute code+ h3("Connection successfully set."), |
||
708 | -+ | |||
135 | +! |
- # @return (`environment`) which stores modified `x`+ p("You can close this window and get back to R console.") |
||
709 | +136 |
- execute_code = function(code, vars = list()) {- |
- ||
710 | -81x | -
- stopifnot(inherits(code, "CodeClass"))+ ) |
||
711 | -81x | +|||
137 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ ) |
|||
712 | +138 |
-
+ ) |
||
713 | -81x | +|||
139 | +
- execution_environment <- new.env(parent = parent.env(globalenv()))+ ) |
|||
714 | +140 |
-
+ ) |
||
715 | +141 |
- # set up environment for execution+ ), |
||
716 | -81x | +|||
142 | +! |
- for (vars_idx in seq_along(vars)) {+ server = function(input, output, session) { |
||
717 | -126x | +|||
143 | +! |
- var_name <- names(vars)[[vars_idx]]+ session$onSessionEnded(stopApp) |
||
718 | -126x | +|||
144 | +! |
- var_value <- vars[[vars_idx]]+ preopen_server <- self$get_preopen_server() |
||
719 | -126x | +|||
145 | +! |
- if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) {+ if (!is.null(preopen_server)) { |
||
720 | -106x | +|||
146 | +! |
- var_value <- get_raw_data(var_value)+ preopen_server(id = "data_connection", connection = self) |
||
721 | +147 |
- }+ } |
||
722 | -126x | +|||
148 | +! |
- assign(envir = execution_environment, x = var_name, value = var_value)+ observeEvent(input$submit, { |
||
723 | -+ | |||
149 | +! |
- }+ rv <- reactiveVal(NULL) |
||
724 | -+ | |||
150 | +! |
-
+ open_server <- self$get_open_server() |
||
725 | -+ | |||
151 | +! |
- # execute+ if (!is.null(open_server)) { |
||
726 | -81x | +|||
152 | +! |
- code$eval(envir = execution_environment)+ rv(open_server(id = "data_connection", connection = self)) |
||
727 | +153 |
-
+ } |
||
728 | -77x | +|||
154 | +! |
- if (!is.data.frame(execution_environment[[self$get_dataname()]])) {+ observeEvent(rv(), { |
||
729 | -1x | +|||
155 | +! |
- out_msg <- sprintf(+ if (self$is_opened()) { |
||
730 | -1x | +|||
156 | +! |
- "\n%s\n\n - Code from %s need to return a data.frame assigned to an object of dataset name.",+ removeUI(sprintf("#%s", session$ns("connection_inputs"))) |
||
731 | -1x | +|||
157 | +! |
- self$get_code(),+ shinyjs::show("connection_set") |
||
732 | -1x | +|||
158 | +! |
- self$get_dataname()+ stopApp() |
||
733 | +159 |
- )+ } |
||
734 | +160 | - - | -||
735 | -1x | -
- rlang::with_options(- |
- ||
736 | -1x | -
- .expr = stop(out_msg, call. = FALSE),- |
- ||
737 | -1x | -
- warning.length = max(min(8170, nchar(out_msg) + 30), 100)+ }) |
||
738 | +161 |
- )+ }) |
||
739 | +162 |
- }+ } |
||
740 | +163 | - - | -||
741 | -76x | -
- new_set <- execution_environment[[self$get_dataname()]]+ ) |
||
742 | +164 | - - | -||
743 | -76x | -
- return(new_set)+ }, |
||
744 | +165 |
- },+ # ___ open connection ----- |
||
745 | +166 |
-
+ #' @description |
||
746 | +167 |
- # Set the name for the dataset+ #' Open the connection. |
||
747 | +168 |
- # @param `dataname` (`character`) the new name+ #' |
||
748 | +169 |
- # @return self invisibly for chaining+ #' Note that if the connection is already opened then it does nothing. |
||
749 | +170 |
- set_dataname = function(dataname) {+ #' |
||
750 | -505x | +|||
171 | +
- check_simple_name(dataname)+ #' @param args (`NULL` or named `list`) additional arguments not set up previously |
|||
751 | -505x | +|||
172 | +
- private$dataname <- dataname+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
|||
752 | -505x | +|||
173 | +
- return(invisible(self))+ #' @param try (`logical`) whether perform function evaluation inside `try` clause |
|||
753 | +174 |
- },+ #' |
||
754 | +175 |
- set_var_r6 = function(vars) {+ #' @return returns `self` if successful or if connection has been already |
||
755 | -671x | +|||
176 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' opened. If `open_fun` fails, app returns an error in form of |
|||
756 | -671x | +|||
177 | +
- for (varname in names(vars)) {+ #' `shinyjs::alert` (if `try = TRUE`) or breaks the app (if `try = FALSE`) |
|||
757 | -90x | +|||
178 | +
- var <- vars[[varname]]+ #' |
|||
758 | +179 |
-
+ open = function(args = NULL, silent = FALSE, try = FALSE) { |
||
759 | -90x | +180 | +6x |
- if (inherits(var, "TealDatasetConnector") || inherits(var, "TealDataset")) {+ logger::log_trace("TealDataConnection$open opening the connection...") |
760 | -64x | +181 | +6x |
- var_deps <- var$get_var_r6()+ checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE) |
761 | -64x | +182 | +6x |
- var_deps[[varname]] <- var+ if (isFALSE(private$check_open_fun(silent = silent))) { |
762 | -64x | +|||
183 | +! |
- for (var_dep_name in names(var_deps)) {+ return() |
||
763 | -82x | +|||
184 | +
- var_dep <- var_deps[[var_dep_name]]+ } |
|||
764 | -82x | +185 | +6x |
- if (identical(self, var_dep)) {+ if (isTRUE(private$opened) && isTRUE(private$ping())) { |
765 | -6x | +|||
186 | +! |
- stop("Circular dependencies detected")+ private$opened <- TRUE |
||
766 | -+ | |||
187 | +! |
- }+ logger::log_trace("TealDataConnection$open connection already opened - skipped.") |
||
767 | -76x | +|||
188 | +! |
- private$var_r6[[var_dep_name]] <- var_dep+ return(invisible(self)) |
||
768 | +189 |
- }+ } else { |
||
769 | -+ | |||
190 | +6x |
- }+ open_res <- private$open_fun$run(args = args, try = try) |
||
770 | -+ | |||
191 | +6x |
- }+ if (!self$is_open_failed()) { |
||
771 | -665x | +192 | +6x |
- return(invisible(self))+ private$opened <- TRUE |
772 | -+ | |||
193 | +6x |
- }+ if (private$if_conn_obj && !is.null(open_res)) { |
||
773 | -+ | |||
194 | +! |
- ),+ private$conn <- open_res |
||
774 | +195 |
- ## __Active Fields ====+ |
||
775 | -+ | |||
196 | +! |
- active = list(+ if (!is.null(private$close_fun)) { |
||
776 | -+ | |||
197 | +! |
- #' @field raw_data The data.frame behind this R6 class+ private$close_fun$assign_to_env("conn", private$conn) |
||
777 | +198 |
- raw_data = function() {+ } |
||
778 | -37x | +|||
199 | +! |
- private$.raw_data+ if (!is.null(private$ping_fun)) { |
||
779 | -+ | |||
200 | +! |
- },+ private$ping_fun$assign_to_env("conn", private$conn) |
||
780 | +201 |
- #' @field data The data.frame behind this R6 class+ } |
||
781 | +202 |
- data = function() {+ } |
||
782 | -40x | +203 | +6x |
- private$.raw_data+ logger::log_trace("TealDataConnection$open connection opened.") |
783 | +204 |
- },+ } else { |
||
784 | -+ | |||
205 | +! |
- #' @field var_names The column names of the data+ private$opened <- FALSE |
||
785 | -+ | |||
206 | +! |
- var_names = function() {+ private$conn <- NULL |
||
786 | -37x | +|||
207 | +! |
- colnames(private$.raw_data)+ logger::log_error("TealDataConnection$open connection failed to open.") |
||
787 | +208 |
- }+ } |
||
788 | +209 |
- )+ |
||
789 | -+ | |||
210 | +6x |
- )+ return(invisible(self)) |
||
790 | +211 |
-
+ } |
||
791 | +212 |
- ## Constructors ====+ }, |
||
792 | +213 | |||
793 | +214 |
- #' Constructor for [`TealDataset`] class+ #' @description |
||
794 | +215 |
- #'+ #' Get internal connection object |
||
795 | +216 |
- #' @description `r lifecycle::badge("stable")`+ #' |
||
796 | +217 |
- #'+ #' @return `connection` object |
||
797 | +218 |
- #' @param dataname (`character`) a given name for the dataset, it cannot contain spaces+ get_conn = function() { |
||
798 | -+ | |||
219 | +3x |
- #'+ return(private$conn) |
||
799 | +220 |
- #' @param x (`data.frame` or `MultiAssayExperiment`) object from which the dataset will be created+ }, |
||
800 | +221 |
- #'+ #' @description |
||
801 | +222 |
- #' @param keys optional, (`character`) vector with primary keys+ #' Get executed open connection call |
||
802 | +223 |
- #'+ #' |
||
803 | +224 |
- #' @param code (`character`) a character string defining the code needed to+ #' @param deparse (`logical`) whether return deparsed form of a call |
||
804 | +225 |
- #' produce the data set in `x`+ #' @param args (`NULL` or named `list`) additional arguments not set up previously |
||
805 | +226 |
- #'+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
||
806 | +227 |
- #' @param label (`character`) label to describe the dataset+ #' |
||
807 | +228 |
- #'+ #' @return optionally deparsed `call` object |
||
808 | +229 |
- #' @param vars (named `list`) in case when this object code depends on other `TealDataset`+ get_open_call = function(deparse = TRUE, args = NULL, silent = FALSE) { |
||
809 | -+ | |||
230 | +34x |
- #' object(s) or other constant value, this/these object(s) should be included as named+ checkmate::assert_flag(deparse) |
||
810 | -+ | |||
231 | +34x |
- #' element(s) of the list. For example if this object code needs `ADSL`+ checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE) |
||
811 | -+ | |||
232 | +34x |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ if (isFALSE(private$check_open_fun(silent = silent))) { |
||
812 | -+ | |||
233 | +! |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ return() |
||
813 | +234 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ } |
||
814 | -+ | |||
235 | +34x |
- #' are included to this object as local `vars` and they cannot be modified+ open_call <- private$open_fun$get_call(deparse = FALSE, args = args) |
||
815 | +236 |
- #' within another dataset.+ |
||
816 | -+ | |||
237 | +34x |
- #'+ if (private$if_conn_obj) { |
||
817 | -+ | |||
238 | +! |
- #' @param metadata (named `list` or `NULL`) field containing metadata about the dataset.+ open_call <- call("<-", as.name("conn"), open_call) |
||
818 | +239 |
- #' Each element of the list should be atomic and length one.+ } |
||
819 | +240 |
- #'+ |
||
820 | -+ | |||
241 | +34x |
- #' @return [`TealDataset`] object+ if (isTRUE(deparse)) { |
||
821 | -+ | |||
242 | +32x |
- #'+ deparse1(open_call, collapse = "\n") |
||
822 | +243 |
- #' @rdname dataset+ } else { |
||
823 | -+ | |||
244 | +2x |
- #'+ open_call |
||
824 | +245 |
- #' @export+ } |
||
825 | +246 |
- #'+ }, |
||
826 | +247 |
- #' @examples+ #' @description |
||
827 | +248 |
- #' # Simple example+ #' Get error message from last connection |
||
828 | +249 |
- #' dataset("iris", iris)+ #' |
||
829 | +250 |
- #'+ #' @return (`character`)\cr |
||
830 | +251 |
- #' # Example with more arguments+ #' text of the error message or `character(0)` if last |
||
831 | +252 |
- #' \dontrun{+ #' connection was successful. |
||
832 | +253 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ get_open_error_message = function() { |
||
833 | -+ | |||
254 | +! |
- #' ADSL_dataset <- dataset(dataname = "ADSL", x = ADSL)+ return(private$open_fun$get_error_message()) |
||
834 | +255 |
- #'+ }, |
||
835 | +256 |
- #' ADSL_dataset$get_dataname()+ #' @description |
||
836 | +257 |
- #'+ #' Get shiny server module prior opening connection. |
||
837 | +258 |
- #' ADSL_dataset <- dataset(+ #' |
||
838 | +259 |
- #' dataname = "ADSL",+ #' @return (`function`) shiny server prior opening connection. |
||
839 | +260 |
- #' x = ADSL,+ get_preopen_server = function() {+ |
+ ||
261 | +! | +
+ return(private$preopen_server) |
||
840 | +262 |
- #' label = "AdAM subject-level dataset",+ }, |
||
841 | +263 |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ #' @description |
||
842 | +264 |
- #' )+ #' Get shiny server module to open connection. |
||
843 | +265 |
- #' ADSL_dataset$get_metadata()+ #' |
||
844 | +266 |
- #' ADSL_dataset$get_dataset_label()+ #' @return (`function`) shiny server to open connection. |
||
845 | +267 |
- #' ADSL_dataset$get_code()+ get_open_server = function() {+ |
+ ||
268 | +! | +
+ return(private$open_server) |
||
846 | +269 |
- #' }+ }, |
||
847 | +270 |
- dataset <- function(dataname,+ #' @description |
||
848 | +271 |
- x,+ #' Get Shiny module with inputs to open connection |
||
849 | +272 |
- keys = character(0),+ #' |
||
850 | +273 |
- label = data_label(x),+ #' @param id `character` shiny element id |
||
851 | +274 |
- code = character(0),+ #' |
||
852 | +275 |
- vars = list(),+ #' @return (`function`) shiny UI to set arguments to open connection function. |
||
853 | +276 |
- metadata = NULL) {+ get_open_ui = function(id) { |
||
854 | -258x | +|||
277 | +! |
- UseMethod("dataset", x)+ return(private$open_ui(id)) |
||
855 | +278 |
- }+ }, |
||
856 | +279 |
-
+ #' @description |
||
857 | +280 |
- #' @rdname dataset+ #' Check if open connection has not failed. |
||
858 | +281 |
- #' @export+ #' |
||
859 | +282 |
- dataset.data.frame <- function(dataname,+ #' @return (`logical`) `TRUE` if open connection failed, else `FALSE` |
||
860 | +283 |
- x,+ is_open_failed = function() { |
||
861 | -+ | |||
284 | +6x |
- keys = character(0),+ if (!is.null(private$open_fun)) { |
||
862 | -+ | |||
285 | +6x |
- label = data_label(x),+ private$open_fun$is_failed() |
||
863 | +286 |
- code = character(0),+ } else {+ |
+ ||
287 | +! | +
+ FALSE |
||
864 | +288 |
- vars = list(),+ } |
||
865 | +289 |
- metadata = NULL) {+ }, |
||
866 | -254x | +|||
290 | +
- checkmate::assert_string(dataname)+ #' @description |
|||
867 | -254x | +|||
291 | +
- checkmate::assert_data_frame(x)+ #' Set open connection function argument |
|||
868 | -254x | +|||
292 | +
- checkmate::assert(+ #' |
|||
869 | -254x | +|||
293 | +
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ #' @param args (`NULL` or named `list`) with values where list names are argument names |
|||
870 | -254x | +|||
294 | +
- checkmate::check_class(code, "CodeClass")+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
|||
871 | +295 |
- )+ #' |
||
872 | -254x | +|||
296 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' @return (`self`) invisibly for chaining. |
|||
873 | +297 |
-
+ set_open_args = function(args, silent = FALSE) { |
||
874 | -254x | +298 | +2x |
- TealDataset$new(+ checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE) |
875 | -254x | +299 | +2x |
- dataname = dataname,+ if (isFALSE(private$check_open_fun(silent = silent))) { |
876 | -254x | +|||
300 | +! |
- x = x,+ return() |
||
877 | -254x | +|||
301 | +
- keys = keys,+ } |
|||
878 | -254x | +302 | +2x |
- code = code,+ private$open_fun$set_args(args) |
879 | -254x | +303 | +2x |
- label = label,+ logger::log_trace("TealDataConnection$set_open_args open args set.") |
880 | -254x | +|||
304 | +
- vars = vars,+ |
|||
881 | -254x | +305 | +2x |
- metadata = metadata+ return(invisible(self)) |
882 | +306 |
- )+ }, |
||
883 | +307 |
- }+ #' @description |
||
884 | +308 |
-
+ #' Set pre-open connection server function |
||
885 | +309 |
- #' Load `TealDataset` object from a file+ #' |
||
886 | +310 |
- #'+ #' This function will be called before submit button will be hit. |
||
887 | +311 |
- #' @description `r lifecycle::badge("experimental")`+ #' |
||
888 | +312 |
- #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise.+ #' @param preopen_module (`function`)\cr |
||
889 | +313 |
- #'+ #' A shiny module server function |
||
890 | +314 |
- #' @param path (`character`) string giving the pathname of the file to read from.+ #' |
||
891 | +315 |
- #' @param code (`character`) reproducible code to re-create object+ #' @return (`self`) invisibly for chaining. |
||
892 | +316 |
- #'+ set_preopen_server = function(preopen_module) { |
||
893 | -+ | |||
317 | +2x |
- #' @return `TealDataset` object+ stopifnot(inherits(preopen_module, "function")) |
||
894 | -+ | |||
318 | +2x |
- #'+ module_name <- "open_conn" |
||
895 | -+ | |||
319 | +2x |
- #' @export+ if (all(names(formals(preopen_module)) %in% c("input", "output", "session", "connection"))) { |
||
896 | -+ | |||
320 | +1x |
- #'+ private$preopen_server <- function(input, output, session, connection) { |
||
897 | -+ | |||
321 | +! |
- #' @examples+ callModule(preopen_module, id = module_name, connection = connection) |
||
898 | +322 |
- #' # simple example+ } |
||
899 | -+ | |||
323 | +1x |
- #' file_example <- tempfile(fileext = ".R")+ } else if (all(names(formals(preopen_module)) %in% c("id", "connection"))) { |
||
900 | -+ | |||
324 | +1x |
- #' writeLines(+ private$preopen_server <- function(id, connection) { |
||
901 | -+ | |||
325 | +! |
- #' text = c(+ moduleServer( |
||
902 | -+ | |||
326 | +! |
- #' "library(teal.data)+ id = id, |
||
903 | -+ | |||
327 | +! |
- #' dataset(dataname = \"iris\",+ module = function(input, output, session) { |
||
904 | -+ | |||
328 | +! |
- #' x = iris,+ preopen_module(id = module_name, connection = connection) |
||
905 | +329 |
- #' code = \"iris\")"+ } |
||
906 | +330 |
- #' ),+ ) |
||
907 | +331 |
- #' con = file_example+ } |
||
908 | +332 |
- #' )+ } else { |
||
909 | -+ | |||
333 | +! |
- #' x <- dataset_file(file_example, code = character(0))+ stop(paste( |
||
910 | -+ | |||
334 | +! |
- #' get_code(x)+ "set_preopen_server accepts only a valid shiny module", |
||
911 | -+ | |||
335 | +! |
- #'+ "definition with a single additional parameter 'connection'." |
||
912 | +336 |
- #' # custom code+ )) |
||
913 | +337 |
- #' file_example <- tempfile(fileext = ".R")+ } |
||
914 | -+ | |||
338 | +2x |
- #' writeLines(+ logger::log_trace("TealDataConnection$set_preopen_server preopen_server set.") |
||
915 | +339 |
- #' text = c(+ |
||
916 | -+ | |||
340 | +2x |
- #' "library(teal.data)+ invisible(self) |
||
917 | +341 |
- #'+ }, |
||
918 | +342 |
- #' # code>+ #' @description |
||
919 | +343 |
- #' x <- iris+ #' Set open connection server function |
||
920 | +344 |
- #' x$a1 <- 1+ #' |
||
921 | +345 |
- #' x$a2 <- 2+ #' This function will be called after submit button will be hit. There is no possibility to |
||
922 | +346 |
- #'+ #' specify some dynamic `ui` as `server` function is executed after hitting submit |
||
923 | +347 |
- #' # <code+ #' button. |
||
924 | +348 |
- #' dataset(dataname = \"iris_mod\", x = x)"+ #' |
||
925 | +349 |
- #' ),+ #' @param open_module (`function`)\cr |
||
926 | +350 |
- #' con = file_example+ #' A shiny module server function that should load data from all connectors |
||
927 | +351 |
- #' )+ #' |
||
928 | +352 |
- #' x <- dataset_file(file_example)+ #' @return (`self`) invisibly for chaining. |
||
929 | +353 |
- #' get_code(x)+ set_open_server = function(open_module) { |
||
930 | -+ | |||
354 | +2x |
- dataset_file <- function(path, code = get_code(path)) {+ stopifnot(inherits(open_module, "function")) |
||
931 | +355 | 2x |
- object <- object_file(path, "TealDataset")+ module_name <- "open_conn" |
|
932 | -1x | +356 | +2x |
- object$set_code(code)+ if (all(names(formals(open_module)) %in% c("input", "output", "session", "connection"))) { |
933 | +357 | 1x |
- return(object)+ private$open_server <- function(input, output, session, connection) {+ |
+ |
358 | +! | +
+ withProgress(message = "Opening connection", value = 1, {+ |
+ ||
359 | +! | +
+ callModule(open_module, id = module_name, connection = connection) |
||
934 | +360 |
- }+ }) |
1 | +361 |
- #' Get code+ }+ |
+ ||
362 | +1x | +
+ } else if (all(names(formals(open_module)) %in% c("id", "connection"))) {+ |
+ ||
363 | +1x | +
+ private$open_server <- function(id, connection) {+ |
+ ||
364 | +! | +
+ moduleServer(+ |
+ ||
365 | +! | +
+ id = id,+ |
+ ||
366 | +! | +
+ module = function(input, output, session) {+ |
+ ||
367 | +! | +
+ withProgress(message = "Opening connection", value = 1, {+ |
+ ||
368 | +! | +
+ open_module(id = module_name, connection = connection) |
||
2 | +369 |
- #'+ }) |
||
3 | +370 |
- #' @description `r lifecycle::badge("stable")`+ } |
||
4 | +371 |
- #' Reads code from specified files or an R6 object.+ ) |
||
5 | +372 |
- #'+ } |
||
6 | +373 |
- #' \itemize{+ } else {+ |
+ ||
374 | +! | +
+ stop(paste(+ |
+ ||
375 | +! | +
+ "set_open_server accepts only a valid shiny module",+ |
+ ||
376 | +! | +
+ "definition with a single additional parameter 'connection'." |
||
7 | +377 |
- #' \item{if reading from R6: }{get the R code stored inside the object.}+ )) |
||
8 | +378 |
- #' \item{if reading from files: }{+ }+ |
+ ||
379 | +2x | +
+ logger::log_trace("TealDataConnection$set_open_server open_server set.") |
||
9 | +380 |
- #' Includes code from source if reading from files. Method reads code without+ + |
+ ||
381 | +2x | +
+ invisible(self) |
||
10 | +382 |
- #' }+ }, |
||
11 | +383 |
- #' }+ #' @description |
||
12 | +384 |
- #' `library()` or `require()` calls. Function created for teal app, but can be used with any file.+ #' Set open connection UI function |
||
13 | +385 |
- #' Get code from certain files and for specific datasets+ #' |
||
14 | +386 |
- #'+ #' @param open_module (`function`)\cr |
||
15 | +387 |
- #' Reads code from specified files and specific code chunks.+ #' shiny module as function. Inputs specified in this `ui` are passed to server module |
||
16 | +388 |
- #'+ #' defined by `set_open_server` method. |
||
17 | +389 |
- #' Code chunks are described with:+ #' |
||
18 | +390 |
- #'+ #' @return (`self`) invisibly for chaining. |
||
19 | +391 |
- #' \itemize{+ set_open_ui = function(open_module) {+ |
+ ||
392 | +! | +
+ stopifnot(inherits(open_module, "function"))+ |
+ ||
393 | +! | +
+ stopifnot(identical(names(formals(open_module)), "id")) |
||
20 | +394 |
- #' \item{to open chunk }{`#code>` or `#code ADSL>` or `#code ADSL ADTTE>`}+ |
||
21 | -+ | |||
395 | +! |
- #' \item{to close chunk }{`#<code` or `#<ADSL code` or `#<ADSL ADTTE code`}+ private$open_ui <- function(id) { |
||
22 | -+ | |||
396 | +! |
- #' }+ ns <- NS(id) |
||
23 | -+ | |||
397 | +! |
- #'+ tags$div( |
||
24 | -+ | |||
398 | +! |
- #' @param x ([`TealDatasetConnector`] or [`TealDataset`]). If of class `character` will be treated as file to read.+ tags$div( |
||
25 | -+ | |||
399 | +! |
- #' @param exclude_comments (`logical`) whether exclude commented-out lines of code. Lines to be excluded+ id = ns("open_conn"), |
||
26 | -+ | |||
400 | +! |
- #' should be ended with `# nocode`. For multiple line exclusions one should enclose ignored block of code with+ open_module(id = ns("open_conn")) |
||
27 | +401 |
- #' `# nocode>` and `# <nocode`+ ) |
||
28 | +402 |
- #' @param read_sources (`logical`) whether to replace `source("path")` with code lines from sourced file.+ ) |
||
29 | +403 |
- #' If `read_sources = TRUE` changing working directory inside preprocessing is not allowed.+ } |
||
30 | -+ | |||
404 | +! |
- #' @param deparse (`logical`) whether return deparsed form of a call+ logger::log_trace("TealDataConnection$set_open_ui open_ui set.") |
||
31 | +405 |
- #' @param files_path (`character`) (optional) vector of files path to be read for preprocessing. Code from+ |
||
32 | -+ | |||
406 | +! |
- #' multiple files is joined together.+ invisible(self) |
||
33 | +407 |
- #' @param dataname (`character`) Name of dataset to return code for.+ }, |
||
34 | +408 |
- #' @param ... not used, only for support of S3+ # ___ close connection ------- |
||
35 | +409 |
- #' @export+ #' @description |
||
36 | +410 |
- #' @return (`character`) code of import and preparation of data for teal application.+ #' Close the connection. |
||
37 | +411 |
- get_code <- function(x, ...) {- |
- ||
38 | -59x | -
- UseMethod("get_code")+ #' |
||
39 | +412 |
- }+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
||
40 | +413 |
-
+ #' @param try (`logical`) whether perform function evaluation inside `try` clause |
||
41 | +414 |
-
+ #' |
||
42 | +415 |
- # Getting code from R6 ====+ #' @return returns (`self`) if successful. For unsuccessful evaluation it |
||
43 | +416 |
-
+ #' depends on `try` argument: if `try = TRUE` then returns |
||
44 | +417 |
- #' @export+ #' `error`, for `try = FALSE` otherwise |
||
45 | +418 |
- #' @rdname get_code+ close = function(silent = FALSE, try = FALSE) { |
||
46 | -+ | |||
419 | +33x |
- get_code.TealDatasetConnector <- function(x, deparse = TRUE, ...) {+ logger::log_trace("TealDataConnection$close closing the connection...") |
||
47 | -5x | +420 | +33x |
- check_ellipsis(...)+ if (isFALSE(private$check_close_fun(silent = silent))) { |
48 | -5x | +421 | +29x |
- x$get_code(deparse = deparse)+ return() |
49 | +422 |
- }+ } |
||
50 | -+ | |||
423 | +4x |
-
+ close_res <- private$close_fun$run(try = try) |
||
51 | -+ | |||
424 | +4x |
- #' @export+ if (inherits(close_res, "error")) { |
||
52 | -+ | |||
425 | +! |
- #' @rdname get_code+ logger::log_error("TealDataConnection$close failed to close the connection.")+ |
+ ||
426 | +! | +
+ return(close_res) |
||
53 | +427 |
- get_code.TealDataset <- function(x, deparse = TRUE, ...) {+ } else { |
||
54 | -12x | +428 | +4x |
- check_ellipsis(...)+ private$opened <- FALSE |
55 | -12x | +429 | +4x |
- x$get_code(deparse = deparse)+ private$conn <- NULL |
56 | -+ | |||
430 | +4x |
- }+ logger::log_trace("TealDataConnection$close connection closed.") |
||
57 | -+ | |||
431 | +4x |
-
+ return(invisible(NULL)) |
||
58 | +432 |
-
+ } |
||
59 | +433 |
- #' @rdname get_code+ }, |
||
60 | +434 |
- #' @export+ #' @description |
||
61 | +435 |
- #' @examples+ #' Get executed close connection call |
||
62 | +436 |
- #' x1 <- dataset(+ #' |
||
63 | +437 |
- #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE),+ #' @param deparse (`logical`) whether return deparsed form of a call |
||
64 | +438 |
- #' keys = "y",+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
||
65 | +439 |
- #' dataname = "XY",+ #' |
||
66 | +440 |
- #' code = "XY <- data.frame(x = c(1, 2), y = c('aa', 'bb'), stringsAsFactors = FALSE)",+ #' @return optionally deparsed `call` object |
||
67 | +441 |
- #' label = character(0)+ get_close_call = function(deparse = TRUE, silent = FALSE) { |
||
68 | -+ | |||
442 | +30x |
- #' )+ checkmate::assert_flag(deparse) |
||
69 | -+ | |||
443 | +30x |
- #'+ if (isFALSE(private$check_close_fun(silent = silent))) { |
||
70 | -+ | |||
444 | +30x |
- #' x2 <- dataset(+ return() |
||
71 | +445 |
- #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE),+ } |
||
72 | -+ | |||
446 | +! |
- #' keys = "y",+ private$close_fun$get_call(deparse = deparse) |
||
73 | +447 |
- #' dataname = "XYZ",+ }, |
||
74 | +448 |
- #' code = "XYZ <- data.frame(x = c(1, 2), y = c('aa', 'bb'), stringsAsFactors = FALSE)",+ #' @description |
||
75 | +449 |
- #' label = character(0)+ #' Get error message from last connection |
||
76 | +450 |
- #' )+ #' |
||
77 | +451 |
- #'+ #' @return (`character`)\cr |
||
78 | +452 |
- #' rd <- teal_data(x1, x2)+ #' text of the error message or `character(0)` if last |
||
79 | +453 |
- #'+ #' connection was successful. |
||
80 | +454 |
- #' get_code(rd)+ get_close_error_message = function() { |
||
81 | -+ | |||
455 | +! |
- #' get_code(rd, "XY")+ return(private$close_fun$get_error_message()) |
||
82 | +456 |
- #' get_code(rd, "XYZ")+ }, |
||
83 | +457 |
- get_code.TealDataAbstract <- function(x, dataname = character(0), deparse = TRUE, ...) { # nolint- |
- ||
84 | -7x | -
- check_ellipsis(...)- |
- ||
85 | -7x | -
- if (length(dataname) > 0) {- |
- ||
86 | -4x | -
- if (any(!(dataname %in% x$get_datanames()))) {+ #' @description |
||
87 | -! | +|||
458 | +
- stop("The dataname provided does not exist")+ #' Get shiny server module to close connection. |
|||
88 | +459 |
- }+ #' |
||
89 | -4x | +|||
460 | +
- x$get_code(dataname = dataname, deparse = deparse)+ #' @return the `server function` to close connection. |
|||
90 | +461 |
- } else {+ get_close_server = function() { |
||
91 | -3x | +|||
462 | +! |
- x$get_code(deparse = deparse)+ return(private$close_server) |
||
92 | +463 |
- }+ }, |
||
93 | +464 |
- }+ #' @description |
||
94 | +465 |
-
+ #' Check if close connection has not failed. |
||
95 | +466 |
- # Getting code from files ====+ #' |
||
96 | +467 |
-
+ #' @return (`logical`) `TRUE` if close connection failed, else `FALSE` |
||
97 | +468 |
- #' @rdname get_code+ is_close_failed = function() { |
||
98 | -+ | |||
469 | +! |
- #' @export+ if (!is.null(private$close_fun)) { |
||
99 | -+ | |||
470 | +! |
- get_code.default <- function(x,+ private$close_fun$is_failed() |
||
100 | +471 |
- exclude_comments = TRUE,+ } else { |
||
101 | -+ | |||
472 | +! |
- read_sources = TRUE,+ FALSE |
||
102 | +473 |
- deparse = FALSE,+ } |
||
103 | +474 |
- files_path = NULL,+ }, |
||
104 | +475 |
- dataname = NULL,+ |
||
105 | +476 |
- ...) {- |
- ||
106 | -35x | -
- if (!is.null(files_path)) {- |
- ||
107 | -31x | -
- x <- files_path+ #' @description |
||
108 | +477 |
- }+ #' Set close connection function argument |
||
109 | +478 | - - | -||
110 | -35x | -
- check_ellipsis(...)- |
- ||
111 | -35x | -
- checkmate::assert_character(x, min.len = 1, any.missing = FALSE)+ #' |
||
112 | -35x | +|||
479 | +
- checkmate::assert_flag(exclude_comments)+ #' @param args (named `list`) with values where list names are argument names |
|||
113 | -32x | +|||
480 | +
- checkmate::assert_flag(read_sources)+ #' @param silent (`logical`) whether convert all "missing function" errors to messages |
|||
114 | +481 |
-
+ #' |
||
115 | -30x | +|||
482 | +
- if (!methods::hasArg(dataname)) {+ #' @return (`self`) invisibly for chaining. |
|||
116 | -11x | +|||
483 | +
- l_lines <- lapply(x, function(file_path) {+ set_close_args = function(args, silent = FALSE) { |
|||
117 | -11x | +|||
484 | +! |
- code_exclude(+ checkmate::assert_list(args, min.len = 0, names = "unique", null.ok = TRUE) |
||
118 | -11x | +|||
485 | +! |
- enclosed_with(+ if (isFalse(private$check_close_fun(silent = silent))) { |
||
119 | -11x | +|||
486 | +! |
- get_code_single(file_path, read_sources = read_sources)+ return() |
||
120 | +487 |
- ),+ } |
||
121 | -11x | +|||
488 | +! |
- lines,+ private$close_fun$set_args(args) |
||
122 | -11x | +|||
489 | +! |
- exclude_comments = exclude_comments+ logger::log_trace("TealDataConnection$set_close_args close_args set") |
||
123 | +490 |
- )+ |
||
124 | -+ | |||
491 | +! |
- })+ return(invisible(self)) |
||
125 | +492 |
- } else {+ }, |
||
126 | -19x | +|||
493 | +
- l_lines <- lapply(x, function(file_path) {+ |
|||
127 | -19x | +|||
494 | +
- code_exclude(+ #' @description |
|||
128 | -19x | +|||
495 | +
- enclosed_with_dataname(+ #' Set close connection UI function |
|||
129 | -19x | +|||
496 | +
- get_code_single(file_path, read_sources = read_sources),+ #' |
|||
130 | -19x | +|||
497 | +
- dataname = dataname+ #' @param close_module (`function`)\cr |
|||
131 | +498 |
- ),+ #' shiny module as function. Inputs specified in this `ui` are passed to server module |
||
132 | -19x | +|||
499 | +
- lines,+ #' defined by `set_close_server` method. |
|||
133 | -19x | +|||
500 | +
- exclude_comments = exclude_comments+ #' |
|||
134 | +501 |
- )+ #' @return (`self`) invisibly for chaining. |
||
135 | +502 |
- })+ set_close_ui = function(close_module) { |
||
136 | -+ | |||
503 | +! |
- }+ stopifnot(inherits(close_module, "function"))+ |
+ ||
504 | +! | +
+ stopifnot(identical(names(formals(close_module)), "id")) |
||
137 | +505 | |||
138 | -27x | +|||
506 | +! |
- lines <- unlist(l_lines)+ private$close_ui <- function(id) { |
||
139 | -27x | +|||
507 | +! |
- if (deparse) {+ ns <- NS(id) |
||
140 | +508 | ! |
- return(paste(+ tags$div( |
|
141 | +509 | ! |
- vapply(lines, FUN = deparse1, collapse = "\n", FUN.VALUE = character(1)),+ tags$div( |
|
142 | +510 | ! |
- collapse = "\n"+ id = ns("close_conn"), |
|
143 | -+ | |||
511 | +! |
- ))+ close_module(id = ns("close_conn")) |
||
144 | +512 |
- } else {+ ) |
||
145 | -27x | +|||
513 | +
- return(paste(lines, collapse = "\n"))+ ) |
|||
146 | +514 |
- }+ } |
||
147 | -+ | |||
515 | +! |
- }+ logger::log_trace("TealDataConnection$close_ui close_ui set.") |
||
148 | +516 | |||
517 | +! | +
+ return(invisible(self))+ |
+ ||
149 | +518 |
-
+ }, |
||
150 | +519 | |||
151 | +520 |
- # * Sub functions for getting code from files ====+ #' @description |
||
152 | +521 |
-
+ #' Set close-connection server function |
||
153 | +522 |
- #' Get code+ #' |
||
154 | +523 |
- #'+ #' This function will be called after submit button will be hit. There is no possibility to |
||
155 | +524 |
- #' Get code from specified file.+ #' specify some dynamic `ui` as `server` function is executed after hitting submit |
||
156 | +525 |
- #' @param file_path (`character`) path or URL address of the file to be parsed+ #' button. |
||
157 | +526 |
- #' @param if_url (`logical`) (optional) TRUE when URL address is provided+ #' |
||
158 | +527 |
- #' @inheritParams get_code+ #' @param close_module (`function`)\cr |
||
159 | +528 |
- #'+ #' A shiny module server function that should load data from all connectors |
||
160 | +529 |
- #' @return lines (`character`) of preprocessing code+ #' |
||
161 | +530 |
- #' @keywords internal+ #' @return (`self`) invisibly for chaining. |
||
162 | +531 |
- get_code_single <- function(file_path, read_sources, if_url = grepl("^http[s]", file_path)) {+ set_close_server = function(close_module) { |
||
163 | -84x | +532 | +2x |
- checkmate::assert_string(file_path)+ stopifnot(inherits(close_module, "function")) |
164 | -84x | +533 | +2x |
- if (!if_url) {+ if (all(names(formals(close_module)) %in% c("input", "output", "session", "connection"))) { |
165 | -84x | +534 | +1x |
- if (!file.exists(file_path)) {+ function(input, output, session, connection) { |
166 | -2x | +|||
535 | +! |
- stop(+ connection$close(try = TRUE) |
||
167 | -2x | +|||
536 | +
- "Reading preprocessing code from ", file_path, " file failed. ",+ |
|||
168 | -2x | +|||
537 | +! |
- "Please double check if you saved your script."+ if (connection$is_close_failed()) { |
||
169 | -+ | |||
538 | +! |
- )+ shinyjs::alert(+ |
+ ||
539 | +! | +
+ paste(+ |
+ ||
540 | +! | +
+ "Error closing connection\nError message: ",+ |
+ ||
541 | +! | +
+ connection$get_close_error_message() |
||
170 | +542 |
- }+ ) |
||
171 | +543 |
- }+ ) |
||
172 | -82x | +|||
544 | +
- checkmate::assert_flag(read_sources)+ } |
|||
173 | -82x | +|||
545 | +! |
- checkmate::assert_flag(if_url)+ invisible(connection) |
||
174 | +546 |
-
+ } |
||
175 | -82x | +547 | +1x |
- lines <- readLines(file_path)+ } else if (all(names(formals(close_module)) %in% c("id", "connection"))) { |
176 | -82x | +548 | +1x |
- if (read_sources) {+ function(id, connection) { |
177 | -80x | +|||
549 | +! |
- lines <- include_source_code(lines = lines, dir = `if`(if_url, NULL, dirname(file_path)))+ moduleServer( |
||
178 | -+ | |||
550 | +! |
- }+ id, |
||
179 | -+ | |||
551 | +! |
-
+ function(input, output, session) { |
||
180 | -82x | +|||
552 | +! |
- lines+ connection$close(try = TRUE) |
||
181 | +553 |
- }+ |
||
182 | -+ | |||
554 | +! |
-
+ if (connection$is_close_failed()) { |
||
183 | -+ | |||
555 | +! |
- #' Get code enclosed within+ shinyjs::alert( |
||
184 | -+ | |||
556 | +! |
- #'+ paste( |
||
185 | -+ | |||
557 | +! |
- #' Extracts lines from code which are enclosed within regexp starts_at and stops_at+ "Error closing connection\nError message: ", |
||
186 | -+ | |||
558 | +! |
- #' @param lines (`character`) of preprocessing code.+ connection$get_close_error_message() |
||
187 | +559 |
- #' @return (`character`) subset of lines which start and end with preprocessing+ ) |
||
188 | +560 |
- #' start and stop tags.+ ) |
||
189 | +561 |
- #' @keywords internal+ } |
||
190 | -+ | |||
562 | +! |
- enclosed_with <- function(lines) {+ invisible(connection) |
||
191 | -11x | +|||
563 | +
- checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ } |
|||
192 | +564 |
-
+ ) |
||
193 | +565 |
- # set beginning of preprocessing+ } |
||
194 | -9x | +|||
566 | +
- idx_start <- grep("#\\s*code>", lines)+ } else { |
|||
195 | -9x | +|||
567 | +! |
- line_starts <- if (length(idx_start) > 1) {+ stop(paste( |
||
196 | +568 | ! |
- warning("More than one preproc start found - using the first one.")+ "set_close_server accepts only a valid shiny module", |
|
197 | +569 | ! |
- idx_start[1] + 1+ "definition with a single additional parameter 'connection'." |
|
198 | -9x | +|||
570 | +
- } else if (length(idx_start) == 1) {+ ))+ |
+ |||
571 | ++ |
+ } |
||
199 | -7x | +572 | +2x |
- idx_start + 1+ logger::log_trace("TealDataConnection$set_close_server close_server set.") |
200 | +573 |
- } else {+ |
||
201 | +574 | 2x |
- 1L+ invisible(self) |
|
202 | +575 |
- }+ } |
||
203 | +576 |
-
+ ), |
||
204 | +577 |
- # set stop of preprocessing+ ## __Private Fields ==== |
||
205 | -9x | +|||
578 | +
- idx_stop <- grep("#\\s*<code", lines)+ private = list( |
|||
206 | -9x | +|||
579 | +
- line_stops <- if (length(idx_stop) > 1) {+ # callableFunctions |
|||
207 | -! | +|||
580 | +
- warning("More than one preproc stops found - using the last one.")+ open_fun = NULL, |
|||
208 | -! | +|||
581 | +
- utils::tail(idx_stop, 1) - 1+ close_fun = NULL, |
|||
209 | -9x | +|||
582 | +
- } else if (length(idx_stop) == 1) {+ ping_fun = NULL, |
|||
210 | -7x | +|||
583 | +
- idx_stop - 1+ |
|||
211 | +584 |
- } else {+ # connection object |
||
212 | -2x | +|||
585 | +
- length(lines)+ if_conn_obj = FALSE, |
|||
213 | +586 |
- }+ conn = NULL, |
||
214 | +587 | |||
215 | -9x | -
- line_numbers <- seq(line_starts, line_stops)- |
- ||
216 | +588 |
-
+ # shiny elements |
||
217 | -9x | +|||
589 | +
- lines[line_numbers]+ open_ui = NULL, |
|||
218 | +590 |
- }+ close_ui = NULL, |
||
219 | +591 |
-
+ ping_ui = NULL, |
||
220 | +592 |
- #' Get code enclosed within+ preopen_server = NULL, |
||
221 | +593 |
- #'+ open_server = NULL, |
||
222 | +594 |
- #' Extracts lines from code which are enclosed within regexp starts_at and stops_at+ close_server = NULL, |
||
223 | +595 |
- #' @inheritParams enclosed_with+ ping_server = NULL, |
||
224 | +596 |
- #' @param dataname (`character`) metadata for returned lines+ opened = FALSE, |
||
225 | +597 |
- #' @return (`list`) list of lines and their numbers from certain chunks of code at the specific file.+ |
||
226 | +598 |
- #' @keywords internal+ ## __Private Methods ==== |
||
227 | +599 |
- enclosed_with_dataname <- function(lines, dataname = NULL) {+ # need to have a custom deep_clone because one of the key fields are reference-type object |
||
228 | -21x | +|||
600 | +
- checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ # in particular: open_fun is a R6 object that wouldn't be cloned using default clone(deep = T) |
|||
229 | -21x | +|||
601 | +
- if (!checkmate::test_character(dataname, min.len = 1, any.missing = FALSE)) {+ deep_clone = function(name, value) { |
|||
230 | -4x | +|||
602 | +! |
- dataname <- ""+ deep_clone_r6(name, value) |
||
231 | +603 |
- }+ }, |
||
232 | -21x | +|||
604 | +
- dataname <- trimws(dataname)+ check_open_fun = function(silent = FALSE) { |
|||
233 | -21x | +605 | +42x |
- any_chunk <- any(grepl("#\\s*<?\\s*code", lines))+ checkmate::assert_flag(silent) |
234 | +606 | |||
235 | -21x | +607 | +42x |
- if (any_chunk) {+ if (is.null(private$open_fun)) { |
236 | -17x | +|||
608 | +! |
- any_start <- any(grepl(sprintf("#\\s*code[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*>", dataname), lines, perl = TRUE))+ msg <- "Open connection function not set" |
||
237 | -17x | +|||
609 | +! |
- any_stop <- any(grepl(sprintf("#\\s*<[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*(?<![a-zA-Z])code", dataname), lines, perl = TRUE))+ if (silent) { |
||
238 | -+ | |||
610 | +! |
-
+ return(FALSE) |
||
239 | -17x | +|||
611 | +
- if (!(any_start && any_stop)) {+ } else { |
|||
240 | -1x | +|||
612 | +! |
- stop(sprintf("File doesn't contain code marked for this %1$s.\n+ stop(msg) |
||
241 | -1x | +|||
613 | +
- Please use # code %1$s> to indicate which lines should be extracted.", dataname))+ } |
|||
242 | +614 |
- }+ } else {+ |
+ ||
615 | +42x | +
+ return(TRUE) |
||
243 | +616 |
- }+ } |
||
244 | +617 |
-
+ }, |
||
245 | +618 |
- # set beginning of preprocessing+ check_close_fun = function(silent = FALSE) { |
||
246 | -20x | +619 | +63x |
- idx_start <- grep(sprintf("#\\s*code(?:[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*|[\\s]*)>", dataname), lines, perl = TRUE)+ checkmate::assert_flag(silent)+ |
+
620 | ++ | + | ||
247 | -20x | +621 | +63x |
- line_starts <- if (length(idx_start) >= 1) {+ if (is.null(private$close_fun)) { |
248 | -16x | +622 | +59x |
- idx_start + 1+ msg <- "Close connection function not set" |
249 | -+ | |||
623 | +59x |
- } else {+ if (silent) { |
||
250 | -4x | +624 | +59x |
- 1L+ return(FALSE) |
251 | +625 |
- }+ } else { |
||
252 | -+ | |||
626 | +! |
-
+ stop(msg) |
||
253 | +627 |
- # set stop of preprocessing+ } |
||
254 | -20x | +|||
628 | +
- idx_stop <- grep(+ } else { |
|||
255 | -20x | +629 | +4x |
- sprintf("#\\s*<(?:[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*|[\\s]*)(?<![a-zA-Z])code", dataname),+ return(TRUE) |
256 | -20x | +|||
630 | +
- lines,+ } |
|||
257 | -20x | +|||
631 | +
- perl = TRUE+ }, |
|||
258 | +632 |
- )+ # @description |
||
259 | -20x | +|||
633 | +
- line_stops <- if (length(idx_stop) >= 1) {+ # Set close connection function |
|||
260 | -16x | +|||
634 | +
- idx_stop - 1+ # |
|||
261 | +635 |
- } else {+ # @param fun (`Callable`) function to close connection |
||
262 | -4x | +|||
636 | +
- length(lines)+ # |
|||
263 | +637 |
- }+ # @return (`self`) invisibly for chaining. |
||
264 | +638 |
-
+ set_close_fun = function(fun) { |
||
265 | -20x | +639 | +3x |
- if (length(line_starts) != length(line_stops) || any(line_starts > line_stops)) {+ stopifnot(inherits(fun, "Callable")) |
266 | -! | +|||
640 | +3x |
- stop("Number of #code> has to be the same as #<code")+ private$close_fun <- fun |
||
267 | -+ | |||
641 | +3x |
- }+ return(invisible(self)) |
||
268 | +642 |
-
+ }, |
||
269 | +643 |
-
+ # @description |
||
270 | -20x | +|||
644 | +
- ll <- data.frame(line_starts, line_stops)+ # Set open connection function |
|||
271 | +645 |
-
+ # |
||
272 | -20x | +|||
646 | +
- line_numbers <- apply(ll, 1, function(x) seq(x[1], x[2]))+ # @param fun (`Callable`) function to open connection |
|||
273 | +647 |
-
+ # |
||
274 | -20x | +|||
648 | +
- lines_taken <- as.integer(unlist(line_numbers))+ # @return (`self`) invisibly for chaining. |
|||
275 | +649 |
-
+ set_open_fun = function(fun) { |
||
276 | -20x | +650 | +21x |
- res_lines <- lines[lines_taken]+ stopifnot(inherits(fun, "Callable")) |
277 | -+ | |||
651 | +21x |
-
+ private$open_fun <- fun |
||
278 | -20x | +652 | +21x |
- return(res_lines)+ return(invisible(self)) |
279 | +653 |
- }+ }, |
||
280 | +654 |
-
+ # @description |
||
281 | +655 |
- #' Exclude from code+ # Set a ping function |
||
282 | +656 |
- #'+ # |
||
283 | +657 |
- #' Excludes lines from code. It is possible to exclude one line ended by `# nocode`+ # @param fun (`Callable`) function to ping connection |
||
284 | +658 |
- #' @inheritParams enclosed_with+ # |
||
285 | +659 |
- #' @inheritParams get_code+ # @return (`self`) invisibly for chaining. |
||
286 | +660 |
- #' @inheritParams get_code_single+ set_ping_fun = function(fun) {+ |
+ ||
661 | +! | +
+ stopifnot(inherits(fun, "Callable"))+ |
+ ||
662 | +! | +
+ private$ping_fun <- fun+ |
+ ||
663 | +! | +
+ return(invisible(self)) |
||
287 | +664 |
- #' @keywords internal+ }, |
||
288 | +665 |
- code_exclude <- function(lines, exclude_comments, file_path) {+ # @description |
||
289 | -32x | +|||
666 | +
- checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ # Ping the connection. |
|||
290 | -29x | +|||
667 | +
- checkmate::assert_flag(exclude_comments)+ # |
|||
291 | +668 |
-
+ # @return (`logical`) |
||
292 | -29x | +|||
669 | +
- nocode_single <- grep("^.+#[[:space:]]*nocode", lines)+ ping = function() { |
|||
293 | -29x | +670 | +1x |
- nocode_start <- grep("[[:space:]]*#[[:space:]]*nocode[[:space:]]*>+", lines)+ logger::log_trace("TealDataConnection$ping pinging the connection...") |
294 | -29x | +671 | +1x |
- nocode_stop <- grep("[[:space:]]*#[[:space:]]*<+[[:space:]]*nocode[[:space:]]*", lines)+ if (!is.null(private$ping_fun)) { |
295 | -+ | |||
672 | +! |
-
+ ping_res <- isTRUE(private$ping_fun$run()) |
||
296 | -29x | +|||
673 | +! |
- if (length(nocode_start) != length(nocode_stop)) {+ logger::log_trace("TealDataConnection$ping ping result: { ping_res }.") |
||
297 | +674 | ! |
- stop(paste("Unequal number of no-code starts and stops in ", file_path)) # nolint+ return(ping_res) |
|
298 | +675 |
- }+ } else { |
||
299 | -+ | |||
676 | +1x |
-
+ return(invisible(NULL)) |
||
300 | -29x | +|||
677 | +
- nocode_multi <- NULL+ } |
|||
301 | -29x | +|||
678 | +
- if (length(nocode_start) > 0) {+ } |
|||
302 | -10x | +|||
679 | +
- nocode_multi <- unlist(Map(seq, from = nocode_start, to = nocode_stop))+ ) |
|||
303 | +680 |
- }+ ) |
||
304 | +681 | |||
305 | -29x | +|||
682 | +
- nocode <- c(nocode_single, nocode_multi)+ #' The constructor for `TealDataConnection` class. |
|||
306 | +683 |
-
+ #' |
||
307 | -29x | +|||
684 | +
- if (length(nocode) > 0) {+ #' @description `r lifecycle::badge("stable")` |
|||
308 | -19x | +|||
685 | +
- lines <- lines[-nocode]+ #' |
|||
309 | +686 |
- }+ #' @param open_fun (`CallableFunction`) function to open connection |
||
310 | +687 |
-
+ #' @param close_fun (`CallableFunction`) function to close connection |
||
311 | -29x | +|||
688 | +
- if (exclude_comments) {+ #' @param ping_fun (`CallableFunction`) function to ping connection |
|||
312 | -10x | +|||
689 | +
- lines <- grep("^\\s*#.+$", x = lines, invert = TRUE, value = TRUE)+ #' @param if_conn_obj optional, (`logical`) whether to store `conn` object returned from opening |
|||
313 | -10x | +|||
690 | +
- lines <- gsub("(^\\s*#.+$)|(#[^\'\"]*$)", "", x = lines, perl = TRUE)+ #' |
|||
314 | +691 |
- }+ #' @examples |
||
315 | +692 |
-
+ #' open_fun <- callable_function(data.frame) # define opening function |
||
316 | -29x | +|||
693 | +
- lines+ #' open_fun$set_args(list(x = 1:5)) # define fixed arguments to opening function |
|||
317 | +694 |
- }+ #' |
||
318 | +695 |
-
+ #' close_fun <- callable_function(sum) # define closing function |
||
319 | +696 |
- #' Finds lines of code with source call+ #' close_fun$set_args(list(x = 1:5)) # define fixed arguments to closing function |
||
320 | +697 |
#' |
||
321 | +698 |
- #' Finds lines in preprocessing code where `source()` call is located+ #' ping_fun <- callable_function(function() TRUE) |
||
322 | +699 |
- #' @inheritParams enclosed_with+ #' |
||
323 | +700 |
- #' @keywords internal+ #' x <- data_connection( # define connection |
||
324 | +701 |
- find_source_code <- function(lines) {+ #' ping_fun = ping_fun, # define ping function |
||
325 | -80x | +|||
702 | +
- checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ #' open_fun = open_fun, # define opening function |
|||
326 | -80x | +|||
703 | +
- idx <- grep("^[^#]*source\\([\'\"]([A-Za-z0-9_/.]).*\\.R[\'\"].*\\).*$", lines)+ #' close_fun = close_fun # define closing function |
|||
327 | +704 |
-
+ #' ) |
||
328 | -80x | +|||
705 | +
- if (length(idx) == 0) {+ #' |
|||
329 | -45x | +|||
706 | +
- return(idx)+ #' x$set_open_args(args = list(y = letters[1:5])) # define additional arguments if necessary |
|||
330 | +707 |
- }+ #' |
||
331 | +708 |
-
+ #' x$open() # call opening function |
||
332 | -35x | +|||
709 | +
- if (any(grepl("source\\([^)]*chdir\\s*=\\s*T(RUE)*", x = lines[idx]))) {+ #' x$get_open_call() # check reproducible R code |
|||
333 | -! | +|||
710 | +
- stop("Preprocessing doesn't handle source(chdir = TRUE)")+ #' |
|||
334 | +711 |
- }+ #' # get data from connection via TealDataConnector$get_dataset() |
||
335 | +712 |
-
+ #' \dontrun{ |
||
336 | -35x | +|||
713 | +
- if (any(grepl("source\\(.+;\\s*source\\(", x = lines[idx]))) {+ #' x$open(args = list(x = 1:5, y = letters[1:5])) # able to call opening function with arguments |
|||
337 | -! | +|||
714 | +
- stop("Preprocessing doesn't handle multiple sources in one line\n")+ #' x$close() # call closing function |
|||
338 | +715 |
- }+ #' } |
||
339 | +716 |
-
+ #' |
||
340 | -35x | +|||
717 | +
- idx+ #' @return `TealDataConnection` object |
|||
341 | +718 |
- }+ #' @export |
||
342 | +719 |
-
+ data_connection <- function(open_fun = NULL, close_fun = NULL, ping_fun = NULL, if_conn_obj = FALSE) { |
||
343 | -+ | |||
720 | +6x |
- #' Includes source in preprocessing code lines+ TealDataConnection$new( |
||
344 | -+ | |||
721 | +6x |
- #'+ open_fun = open_fun, close_fun = close_fun, ping_fun = ping_fun, if_conn_obj = if_conn_obj |
||
345 | +722 |
- #' @inheritParams enclosed_with+ ) |
||
346 | +723 |
- #' @param dir of the file where source is called from.+ } |
347 | +1 |
- #' @return lines of code with source text included+ #' Helper function to deep copy `R6` object |
||
348 | +2 |
- #' @keywords internal+ #' |
||
349 | +3 |
- include_source_code <- function(lines, dir = NULL) {+ #' When cloning an R6 object the private function |
||
350 | -80x | +|||
4 | +
- checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ #' `deep_clone` is automatically used. To ensure a complete |
|||
351 | -80x | +|||
5 | +
- stopifnot(is.null(dir) || dir.exists(dir))+ #' clone the private function should call this function |
|||
352 | +6 |
-
+ #' |
||
353 | +7 |
-
+ #' @param name (`character`) argument passed by `deep_clone` function. |
||
354 | -80x | +|||
8 | +
- idx <- find_source_code(lines)+ #' @param value (any `R` object) argument passed by `deep_clone` function. |
|||
355 | +9 |
-
+ #' @keywords internal |
||
356 | -80x | +|||
10 | +
- if (length(idx) == 0) {+ deep_clone_r6 <- function(name, value) { |
|||
357 | -45x | +11 | +1629x |
- return(lines)+ if (checkmate::test_list(value, types = "R6")) { |
358 | -+ | |||
12 | +86x |
- }+ lapply(value, function(x) x$clone(deep = TRUE)) |
||
359 | -+ | |||
13 | +1543x |
-
+ } else if (R6::is.R6(value)) { |
||
360 | -35x | +14 | +31x |
- sources_path <- unname(vapply(+ value$clone(deep = TRUE) |
361 | -35x | +15 | +1512x |
- lines[idx],+ } else if (is.environment(value)) { |
362 | -35x | +16 | +5x |
- function(x) {+ new_env <- as.environment(as.list(value, all.names = TRUE)) |
363 | -52x | +17 | +5x |
- res <- gsub("source\\(.*[\"\']([A-Za-z0-9_/.])", "\\1", strsplit(x, ",")[[1]][1])+ parent.env(new_env) <- parent.env(value) |
364 | -52x | +18 | +5x |
- res <- gsub("[\'\"]", "", res)+ new_env |
365 | -52x | +|||
19 | +
- res <- gsub(")", "", res)+ } else { |
|||
366 | -52x | +20 | +1507x |
- res+ value |
367 | +21 |
- },+ } |
||
368 | -35x | +|||
22 | +
- character(1)+ } |
369 | +1 |
- ))+ ## TealDatasetConnector ==== |
|
370 | +2 |
-
+ #' |
|
371 | -35x | +||
3 | +
- if (length(sources_path) != length(idx)) {+ #' |
||
372 | -! | +||
4 | +
- stop("Couldn't detect R file name from source() call.")+ #' @title A `TealDatasetConnector` class of objects |
||
373 | +5 |
- }+ #' |
|
374 | +6 |
-
+ #' @description `r lifecycle::badge("stable")` |
|
375 | -35x | +||
7 | +
- sources_code <- lapply(sources_path, function(s) {+ #' Objects of this class store the connection function to fetch a single dataset. |
||
376 | -52x | +||
8 | +
- if (grepl("^http[s]", s)) {+ #' Note that for some specific connection types, |
||
377 | +9 |
- # url detected - do nothing+ #' an object of class `TealDataConnection` must be provided. |
|
378 | +10 |
- } else {+ #' Data can be pulled via the `pull` method and accessed directly |
|
379 | -52x | +||
11 | +
- s <- ifelse(grepl("^(/)|^([\\])|^([A-Za-z]:)", s), s, file.path(dir, s))+ #' through the `dataset` active binding. |
||
380 | -52x | +||
12 | +
- if (!all(file.exists(s))) {+ #' Pulled data inherits from the class [`TealDataset`] |
||
381 | -! | +||
13 | +
- msg <- paste0(+ #' |
||
382 | -! | +||
14 | +
- "File(s) provided in the source() calls don't exist: \n",+ #' @param dataname (`character`)\cr |
||
383 | -! | +||
15 | +
- paste(s[!file.exists(s)], collapse = "\n")+ #' A given name for the dataset it may not contain spaces |
||
384 | +16 |
- )+ #' |
|
385 | -! | +||
17 | +
- stop(msg)+ #' @param pull_callable (`CallableFunction`)\cr |
||
386 | +18 |
- }+ #' function with necessary arguments set to fetch data from connection. |
|
387 | +19 |
-
+ #' |
|
388 | -52x | +||
20 | +
- s <- normalizePath(s)+ #' @param keys optional, (`character`)\cr |
||
389 | +21 |
- }+ #' vector of dataset primary keys column names |
|
390 | +22 |
-
+ #' |
|
391 | -52x | +||
23 | +
- get_code_single(file_path = s, read_sources = TRUE)+ #' @param label (`character`)\cr |
||
392 | +24 |
- })+ #' Label to describe the dataset. |
|
393 | +25 |
-
+ #' |
|
394 | -35x | +||
26 | +
- lines[idx] <- sources_code+ #' @param code (`character`)\cr |
||
395 | -35x | +||
27 | +
- lines <- unlist(lines)+ #' A character string defining code to modify `raw_data` from this dataset. To modify |
||
396 | +28 |
-
+ #' current dataset code should contain at least one assignment to object defined in `dataname` |
|
397 | -35x | +||
29 | +
- lines+ #' argument. For example if `dataname = ADSL` example code should contain |
||
398 | +30 |
- }+ #' `ADSL <- <some R code>`. Can't be used simultaneously with `script` |
1 | +31 |
- #' This function returns a dummy dataset for testing examples and should only be used within `teal.data`.+ #' |
|
2 | +32 |
- #'+ #' @param script (`character`)\cr |
|
3 | +33 |
- #' It is not meant to retrieve the SCDA dataset, and the dataset itself is not maintained here.+ #' Alternatively to `code` - location of the file containing modification code. |
|
4 | +34 | ++ |
+ #' Can't be used simultaneously with `script`.+ |
+
35 |
#' |
||
5 | +36 |
- #' This function creates a copy of the SCDA data for testing purposes.+ #' @param vars (named `list`)) \cr |
|
6 | +37 |
- #'+ #' In case when this object code depends on other `TealDataset` object(s) or |
|
7 | +38 |
- #' CDISC data includes `ADSL`, `ADAE`, `ADLB`, `ADCM`, `ADEX`, `ADRS`, `ADTR` and `ADTTE`.+ #' other constant value, this/these object(s) should be included as named |
|
8 | +39 |
- #'+ #' element(s) of the list. For example if this object code needs `ADSL` |
|
9 | +40 |
- #' @param dataname name of the `CDISC` dataset+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
|
10 | +41 |
- #'+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
|
11 | +42 |
- #' @return `cdisc_data`+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
|
12 | +43 |
- #'+ #' are included to this object as local `vars` and they cannot be modified |
|
13 | +44 |
- #' @export+ #' within another dataset. |
|
14 | +45 |
- example_cdisc_data <- function(dataname) {+ #' |
|
15 | +46 |
- # Define the available datasets+ #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr |
|
16 | -33x | +||
47 | +
- datasets <- c("ADSL", "ADAE", "ADLB", "ADCM", "ADEX", "ADRS", "ADTR", "ADTTE", "ADVS")+ #' Field containing either the metadata about the dataset (each element of the list |
||
17 | +48 |
-
+ #' should be atomic and length one) or a `CallableFuntion` to pull the metadata |
|
18 | +49 |
- # Check if the provided dataname is valid+ #' from a connection. This should return a `list` or an object which can be |
|
19 | -33x | +||
50 | +
- if (dataname %in% datasets) {+ #' converted to a list with `as.list`. |
||
20 | -33x | +||
51 | +
- dataset <- get(paste0("r", dataname))+ TealDatasetConnector <- R6::R6Class( # nolint |
||
21 | -33x | +||
52 | +
- return(dataset)+ |
||
22 | +53 |
- } else {+ ## __Public Methods ==== |
|
23 | -! | +||
54 | +
- stop("Invalid dataname. Please provide one of the following: ", paste(datasets, collapse = ", "))+ classname = "TealDatasetConnector", |
||
24 | +55 |
- }+ public = list( |
|
25 | +56 |
- }+ #' @description |
1 | +57 |
- #' S3 generic for `to_relational_data` function.+ #' Create a new `TealDatasetConnector` object. Set the pulling function |
||
2 | +58 |
- #'+ #' `CallableFunction` which returns a `data.frame` or `MultiAssayExperiment`, |
||
3 | +59 |
- #' This function takes an object and converts into a `TealData` object, the primary data+ #' e.g. by reading from a function or creating it on the fly. |
||
4 | +60 |
- #' object for use in teal applications.+ initialize = function(dataname, |
||
5 | +61 |
- #'+ pull_callable, |
||
6 | +62 |
- #' @param data `TealDataset`, `TealDatasetConnector`, `data.frame`, `MultiAssayExperiment`, `list`+ keys = character(0), |
||
7 | +63 |
- #' or `function` returning a named list.+ label = character(0), |
||
8 | +64 |
- #'+ code = character(0), |
||
9 | +65 |
- #' @details Passing a `TealData` into this function leaves the object unchanged.+ vars = list(), |
||
10 | +66 |
- #'+ metadata = NULL) {+ |
+ ||
67 | +182x | +
+ private$set_pull_callable(pull_callable)+ |
+ ||
68 | +182x | +
+ private$set_var_r6(vars)+ |
+ ||
69 | +182x | +
+ private$set_pull_vars(vars) |
||
11 | +70 |
- #' @return `TealData` object+ + |
+ ||
71 | +182x | +
+ private$set_dataname(dataname)+ |
+ ||
72 | +182x | +
+ private$set_metadata(metadata) |
||
12 | +73 |
- #'+ + |
+ ||
74 | +182x | +
+ self$set_dataset_label(label)+ |
+ ||
75 | +182x | +
+ self$set_keys(keys) |
||
13 | +76 |
- #' @examples+ + |
+ ||
77 | +182x | +
+ if (length(code) > 0) { |
||
14 | +78 |
- #'+ # just needs a dummy TealDataset object to store mutate code, hence col = 1+ |
+ ||
79 | +1x | +
+ private$dataset <- TealDataset$new(dataname = self$get_dataname(), x = data.frame(col = 1))+ |
+ ||
80 | +1x | +
+ private$dataset$mutate(code = code, vars = vars, force_delay = TRUE) |
||
15 | +81 |
- #' to_relational_data(head(iris))+ } |
||
16 | +82 |
- #' to_relational_data(dataset("IRIS", head(iris)))+ + |
+ ||
83 | +182x | +
+ logger::log_trace("TealDatasetConnector initialized for dataset: { deparse1(self$get_dataname()) }.") |
||
17 | +84 |
- #' to_relational_data(list(iris = head(iris), mtcars = head(mtcars)))+ |
||
18 | -+ | |||
85 | +182x |
- #'+ return(invisible(self)) |
||
19 | +86 |
- #' d_connector <- dataset_connector("iris", callable_function(function() head(iris)))+ }, |
||
20 | +87 |
- #' d_connector$pull()+ #' @description |
||
21 | +88 |
- #' to_relational_data(d_connector)+ #' Prints this `TealDatasetConnector`. |
||
22 | +89 |
- #'+ #' |
||
23 | +90 |
- #' @keywords internal+ #' @param ... additional arguments to the printing method |
||
24 | +91 |
- #' @export+ #' @return invisibly self |
||
25 | +92 |
- to_relational_data <- function(data) {+ print = function(...) { |
||
26 | -18x | -
- UseMethod("to_relational_data")- |
- ||
27 | -+ | 93 | +6x |
- }+ check_ellipsis(...) |
28 | +94 | |||
29 | -- |
- #' @keywords internal- |
- ||
30 | -+ | |||
95 | +6x |
- #' @export+ cat(sprintf( |
||
31 | -+ | |||
96 | +6x |
- to_relational_data.data.frame <- function(data) { # nolint+ "A %s object, named %s, containing a TealDataset object that has %sbeen loaded/pulled%s\n", |
||
32 | -2x | +97 | +6x |
- dataname <- deparse(substitute(data, parent.frame()), width.cutoff = 500L)+ class(self)[1], |
33 | -2x | +98 | +6x |
- if (grepl("\\)$", dataname)) {+ self$get_dataname(), |
34 | -! | +|||
99 | +6x |
- stop("Single data.frame shouldn't be provided as a result of a function call. Please name+ ifelse(self$is_pulled(), "", "not "), |
||
35 | -! | +|||
100 | +6x |
- the object first or use a named list.")+ ifelse(self$is_pulled(), ":", "") |
||
36 | +101 |
- }+ )) |
||
37 | -+ | |||
102 | +6x |
-
+ if (self$is_pulled()) { |
||
38 | +103 | 2x |
- if (dataname %in% names(default_cdisc_keys)) {+ print(self$get_dataset()) |
|
39 | -! | +|||
104 | +
- cdisc_data(cdisc_dataset(dataname, data))+ } |
|||
40 | +105 |
- } else {+ |
||
41 | -2x | +106 | +6x |
- teal_data(dataset(dataname, data))+ invisible(self) |
42 | +107 |
- }+ }, |
||
43 | +108 |
- }+ |
||
44 | +109 |
-
+ # ___ getters ==== |
||
45 | +110 |
- #' @keywords internal+ #' @description |
||
46 | +111 |
- #' @export+ #' Get `dataname` of dataset |
||
47 | +112 |
- to_relational_data.TealDataset <- function(data) {- |
- ||
48 | -4x | -
- dataname <- get_dataname(data)+ #' |
||
49 | +113 | - - | -||
50 | -4x | -
- if (dataname %in% names(default_cdisc_keys)) {- |
- ||
51 | -2x | -
- cdisc_data(data)+ #' @return `dataname` of the dataset |
||
52 | +114 |
- } else {+ get_dataname = function() { |
||
53 | -2x | +115 | +522x |
- teal_data(data)+ return(private$dataname) |
54 | +116 |
- }+ }, |
||
55 | +117 |
- }+ #' @description |
||
56 | +118 |
-
+ #' Get `dataname` of dataset |
||
57 | +119 |
- #' @keywords internal+ #' |
||
58 | +120 |
- #' @export+ #' @return `character` `dataname` of the dataset |
||
59 | +121 |
- to_relational_data.TealDatasetConnector <- function(data) { # nolint+ get_datanames = function() { |
||
60 | -1x | +122 | +11x |
- to_relational_data.TealDataset(data)+ return(private$dataname) |
61 | +123 |
- }+ }, |
||
62 | +124 |
-
+ #' @description |
||
63 | +125 |
- #' @keywords internal+ #' Get label of dataset |
||
64 | +126 |
- #' @export+ #' |
||
65 | +127 |
- to_relational_data.list <- function(data) {- |
- ||
66 | -11x | -
- checkmate::assert_list(+ #' @return `character` dataset label |
||
67 | -11x | +|||
128 | +
- data,+ get_dataset_label = function() { |
|||
68 | -11x | +129 | +132x |
- types = c("dataset", "data.frame", "MultiAssayExperiment", "TealDataset", "TealDatasetConnector")+ return(private$dataset_label) |
69 | +130 |
- )+ }, |
||
70 | +131 | - - | -||
71 | -11x | -
- call <- substitute(data, parent.frame())- |
- ||
72 | -11x | -
- list_names <- names(data)- |
- ||
73 | -11x | -
- parsed_names <- as.character(call)[-1]+ #' @description |
||
74 | +132 |
-
+ #' Get primary keys of dataset |
||
75 | +133 |
- if (+ #' @return `character` vector with dataset primary keys |
||
76 | +134 |
- (- |
- ||
77 | -11x | -
- length(list_names) == 0 &&+ get_keys = function() { |
||
78 | -11x | +135 | +152x |
- length(parsed_names) == 0 &&+ return(private$keys) |
79 | -11x | +|||
136 | +
- any(sapply(data, inherits, c("dataset", "data.frame", "MultiAssayExperiment")))+ }, |
|||
80 | +137 |
- ) ||+ #' @description |
||
81 | -11x | +|||
138 | +
- (any(list_names == "") && length(parsed_names) == 0) ||+ #' Get code to get data |
|||
82 | -11x | +|||
139 | +
- (any(is.na(list_names)))+ #' |
|||
83 | +140 |
- ) {+ #' @param deparse (`logical`)\cr |
||
84 | -3x | +|||
141 | +
- stop("Unnamed lists shouldn't be provided as input for data. Please use a named list.")+ #' whether return deparsed form of a call |
|||
85 | +142 |
- }+ #' |
||
86 | +143 |
-
+ #' @return optionally deparsed `call` object |
||
87 | -8x | +|||
144 | +
- datasets_list <- lapply(+ get_code = function(deparse = TRUE) { |
|||
88 | -8x | +145 | +44x |
- seq_along(data),+ checkmate::assert_flag(deparse) |
89 | -8x | +146 | +44x |
- function(idx) {+ return(self$get_code_class()$get_code(deparse = deparse)) |
90 | -15x | +|||
147 | +
- if (is.data.frame(data[[idx]]) || inherits(data[[idx]], "MultiAssayExperiment")) {+ }, |
|||
91 | -12x | +|||
148 | +
- dataname <- if (length(list_names) == 0 || list_names[[idx]] == "") {+ #' @description |
|||
92 | -3x | +|||
149 | +
- parsed_names[[idx]]+ #' Get internal `CodeClass` object |
|||
93 | +150 |
- } else {+ #' |
||
94 | -9x | +|||
151 | +
- list_names[[idx]]+ #' @return `CodeClass` |
|||
95 | +152 |
- }+ get_code_class = function() { |
||
96 | -+ | |||
153 | +186x |
-
+ code_class <- CodeClass$new() |
||
97 | -12x | +154 | +186x |
- if (dataname %in% names(default_cdisc_keys)) {+ pull_code_class <- private$get_pull_code_class() |
98 | -! | +|||
155 | +186x |
- cdisc_dataset(dataname, data[[idx]])+ code_class$append(pull_code_class) |
||
99 | +156 |
- } else {+ |
||
100 | -12x | -
- dataset(dataname, data[[idx]])- |
- ||
101 | -+ | 157 | +186x |
- }+ if (!is.null(private$dataset)) { |
102 | -3x | +158 | +70x |
- } else if (inherits(data[[idx]], "TealDataset") || inherits(data[[idx]], "TealDatasetConnector")) {+ executed_code_in_dataset <- private$dataset$get_code_class() |
103 | -3x | +159 | +70x |
- data[[idx]]+ code_class$append(executed_code_in_dataset) |
104 | +160 |
- } else {+ } |
||
105 | -! | +|||
161 | +
- stop("Unknown class to create TealDataset from.")+ |
|||
106 | -+ | |||
162 | +186x |
- }+ return(code_class) |
||
107 | +163 |
- }+ }, |
||
108 | +164 |
- )+ #' @description |
||
109 | +165 |
-
+ #' |
||
110 | -8x | +|||
166 | +
- if (any(sapply(datasets_list, function(x) inherits(x, "CDISCTealDataset")))) {+ #' Derive the arguments this connector will pull with |
|||
111 | -! | +|||
167 | +
- do.call("cdisc_data", args = datasets_list)+ #' @return `list` of pull function fixed arguments |
|||
112 | +168 |
- } else {+ get_pull_args = function() { |
||
113 | -8x | +|||
169 | +! |
- do.call("teal_data", args = datasets_list)+ private$pull_callable$get_args() |
||
114 | +170 |
- }+ }, |
||
115 | +171 |
- }+ #' @description |
||
116 | +172 |
-
+ #' Get dataset |
||
117 | +173 |
- #' @keywords internal+ #' |
||
118 | +174 |
- #' @export+ #' @return dataset (`TealDataset`) |
||
119 | +175 |
- to_relational_data.MultiAssayExperiment <- function(data) { # nolint+ get_dataset = function() { |
||
120 | -1x | +176 | +120x |
- dataname <- deparse(substitute(data, parent.frame()), width.cutoff = 500L)+ if (!self$is_pulled()) { |
121 | -1x | -
- if (grepl("\\)$", dataname)) {- |
- ||
122 | -! | -
- stop("Single data.frame shouldn't be provided as a result of a function call. Please name- |
- ||
123 | -! | +177 | +21x |
- the object first or use a named list.")+ stop( |
124 | -+ | |||
178 | +21x |
- }+ sprintf("'%s' has not been pulled yet\n - please use `load_dataset()` first.", self$get_dataname()), |
||
125 | -1x | +179 | +21x |
- teal_data(dataset(dataname, data))+ call. = FALSE |
126 | +180 |
- }+ ) |
1 | +181 |
- # CDISCTealDataConnector ------+ } |
||
2 | -+ | |||
182 | +99x |
- #'+ private$dataset$get_dataset() |
||
3 | -+ | |||
183 | +99x |
- #' @title Manage multiple and `TealDatasetConnector` of the same type.+ return(private$dataset) |
||
4 | +184 |
- #'+ }, |
||
5 | +185 |
- #' @description `r lifecycle::badge("stable")`+ #' @description |
||
6 | +186 |
- #' Class manages `TealDatasetConnector` to specify additional dynamic arguments and to+ #' Get error message from last pull |
||
7 | +187 |
- #' open/close connection.+ #' |
||
8 | +188 |
- #'+ #' @return `character` object with error message or `character(0)` if last |
||
9 | +189 |
- #' @param connection (`TealDataConnection`)\cr+ #' pull was successful. |
||
10 | +190 |
- #' connection to data source+ get_error_message = function() { |
||
11 | -+ | |||
191 | +1x |
- #' @param connectors (`list` of `TealDatasetConnector` elements)\cr+ return(private$pull_callable$get_error_message()) |
||
12 | +192 |
- #' list with dataset connectors+ }, |
||
13 | +193 |
- #'+ #' @description |
||
14 | +194 |
- CDISCTealDataConnector <- R6::R6Class( # nolint+ #' Get pull function |
||
15 | +195 |
- classname = "CDISCTealDataConnector",+ #' |
||
16 | +196 |
- inherit = TealDataConnector,+ #' @return `CallableFunction` |
||
17 | +197 |
-
+ get_pull_callable = function() { |
||
18 | -+ | |||
198 | +28x |
- ## __Public Methods ====+ return(private$pull_callable) |
||
19 | +199 |
- public = list(+ }, |
||
20 | +200 |
#' @description |
||
21 | +201 |
- #' Create a new `CDISCTealDataConnector` object+ #' Get raw data from dataset |
||
22 | +202 |
- initialize = function(connection, connectors) {- |
- ||
23 | -8x | -
- super$initialize(connection = connection, connectors = connectors)+ #' |
||
24 | +203 | - - | -||
25 | -8x | -
- new_parent <- list()+ #' @return `data.frame` or `MultiAssayExperiment` data |
||
26 | -8x | +|||
204 | +
- for (x in connectors) {+ get_raw_data = function() { |
|||
27 | -12x | +205 | +60x |
- x_dataname <- x$get_dataname()+ dataset <- self$get_dataset() |
28 | -12x | +206 | +58x |
- new_parent[[x_dataname]] <- if (inherits(x, "CDISCTealDatasetConnector")) {+ return(dataset$get_raw_data()) |
29 | -12x | +|||
207 | +
- x$get_parent()+ }, |
|||
30 | +208 |
- } else {+ #' @description |
||
31 | -! | +|||
209 | +
- character(0L)+ #' Get the list of dependencies that are `TealDataset` or `TealDatasetConnector` objects |
|||
32 | +210 |
- }+ #' |
||
33 | +211 |
- }+ #' @return `list` |
||
34 | +212 |
-
+ get_var_r6 = function() { |
||
35 | -8x | -
- if (is_dag(new_parent)) {- |
- ||
36 | -! | +213 | +47x |
- stop("Cycle detected in a parent and child dataset graph.")+ return(private$var_r6) |
37 | +214 |
- }+ }, |
||
38 | +215 | |||
39 | -8x | +|||
216 | +
- private$parent <- new_parent+ # ___ setters ==== |
|||
40 | -8x | +|||
217 | +
- logger::log_trace(+ #' @description |
|||
41 | -8x | +|||
218 | +
- "CDISCTealDataConnector initialized with data: { paste(self$get_datanames(), collapse = ' ') }"+ #' Reassign `vars` in this object to keep references up to date after deep clone. |
|||
42 | +219 |
- )+ #' Update is done based on the objects passed in `datasets` argument. Reassignment |
||
43 | -8x | +|||
220 | +
- return(invisible(self))+ #' refers only to the provided `datasets`, other `vars` remains the same. |
|||
44 | +221 |
- },+ #' @param datasets (`named list` of `TealDataset(s)` or `TealDatasetConnector(s)`)\cr |
||
45 | +222 |
- #' @description+ #' objects with valid pointers. |
||
46 | +223 |
- #' Get all datasets parent names+ #' @return NULL invisible |
||
47 | +224 |
- #' @return (named `list`) with dataset name and its corresponding parent dataset name+ reassign_datasets_vars = function(datasets) { |
||
48 | -+ | |||
225 | +7x |
- get_parent = function() {+ logger::log_trace( |
||
49 | -! | +|||
226 | +7x |
- private$parent+ "TealDatasetConnector$reassign_datasets_vars reassigning vars in dataset: { self$get_dataname() }." |
||
50 | +227 |
- }+ ) |
||
51 | -+ | |||
228 | +7x |
- ),+ checkmate::assert_list(datasets, min.len = 0, names = "unique") |
||
52 | +229 | |||
53 | -+ | |||
230 | +7x |
- ## __Private Fields ====+ common_var_r6 <- intersect(names(datasets), names(private$var_r6)) |
||
54 | -+ | |||
231 | +7x |
- private = list(+ private$var_r6[common_var_r6] <- datasets[common_var_r6] |
||
55 | +232 |
- parent = list() # list with dataset names and its parent dataset names+ |
||
56 | -+ | |||
233 | +7x |
- )+ common_vars <- intersect(names(datasets), names(private$pull_vars)) |
||
57 | -+ | |||
234 | +7x |
- )+ private$pull_vars[common_vars] <- datasets[common_vars] |
||
58 | +235 | |||
59 | -+ | |||
236 | +7x |
- #' The constructor of `CDISCTealDataConnector` objects.+ if (!is.null(private$dataset)) { |
||
60 | -+ | |||
237 | +! |
- #'+ private$dataset$reassign_datasets_vars(datasets) |
||
61 | +238 |
- #' @description `r lifecycle::badge("stable")`+ } |
||
62 | -+ | |||
239 | +7x |
- #'+ logger::log_trace( |
||
63 | -+ | |||
240 | +7x |
- #' @param connection (`TealDataConnection`)\cr+ "TealDatasetConnector$reassign_datasets_vars reassigned vars in dataset: { self$get_dataname() }." |
||
64 | +241 |
- #' connection to data source+ ) |
||
65 | +242 |
- #' @param connectors (`list` of `TealDatasetConnector` elements)\cr+ |
||
66 | -+ | |||
243 | +7x |
- #' list with dataset connectors+ invisible(NULL) |
||
67 | +244 |
- #'+ }, |
||
68 | +245 |
- #' @examples+ #' @description |
||
69 | +246 |
- #' adsl_cf <- callable_function(+ #' Set label of the `dataset` object |
||
70 | +247 |
- #' function() as.data.frame(as.list(setNames(nm = get_cdisc_keys("ADSL"))))+ #' |
||
71 | +248 |
- #' )+ #' @return (`self`) invisibly for chaining |
||
72 | +249 |
- #' adae_cf <- callable_function(+ set_dataset_label = function(label) { |
||
73 | -+ | |||
250 | +182x |
- #' function() as.data.frame(as.list(setNames(nm = get_cdisc_keys("ADAE"))))+ if (is.null(label)) { |
||
74 | -+ | |||
251 | +! |
- #' )+ label <- character(0) |
||
75 | +252 |
- #' adsl <- cdisc_dataset_connector(+ } |
||
76 | -+ | |||
253 | +182x |
- #' "ADSL", adsl_cf,+ checkmate::assert_character(label, max.len = 1, any.missing = FALSE) |
||
77 | -+ | |||
254 | +182x |
- #' keys = get_cdisc_keys("ADSL"), parent = character(0)+ private$dataset_label <- label |
||
78 | -+ | |||
255 | +182x |
- #' )+ if (self$is_pulled()) { |
||
79 | -+ | |||
256 | +! |
- #' adae <- cdisc_dataset_connector(+ private$dataset$set_dataset_label(label) |
||
80 | +257 |
- #' "ADAE", adae_cf,+ } |
||
81 | -+ | |||
258 | +182x |
- #' keys = get_cdisc_keys("ADAE"), parent = "ADSL"+ logger::log_trace( |
||
82 | -+ | |||
259 | +182x |
- #' )+ "TealDatasetConnector$set_dataset_label label set for dataset: { deparse1(self$get_dataname()) }." |
||
83 | +260 |
- #' data <- cdisc_data_connector(+ ) |
||
84 | +261 |
- #' connection = data_connection(open_fun = callable_function(function() "open function")),+ |
||
85 | -+ | |||
262 | +182x |
- #' connectors = list(adsl, adae)+ return(invisible(self)) |
||
86 | +263 |
- #' )+ }, |
||
87 | +264 |
- #' @return `CDISCTealDataConnector` object+ #' @description |
||
88 | +265 |
- #' @export+ #' Set new keys |
||
89 | +266 |
- cdisc_data_connector <- function(connection, connectors) {+ #' @return (`self`) invisibly for chaining. |
||
90 | -9x | +|||
267 | +
- stopifnot(inherits(connection, "TealDataConnection"))+ set_keys = function(keys) { |
|||
91 | -7x | +268 | +182x |
- checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1)+ checkmate::assert_character(keys, any.missing = FALSE) |
92 | -5x | +269 | +182x |
- CDISCTealDataConnector$new(connection, connectors)+ if (isTRUE(self$is_pulled())) { |
93 | -+ | |||
270 | +! |
- }+ set_keys(private$dataset, keys) |
1 | +271 |
- #' S3 generic for creating an information summary about the duplicate key values in a dataset+ } |
||
2 | -+ | |||
272 | +182x |
- #'+ private$keys <- keys |
||
3 | -+ | |||
273 | +182x |
- #' @description `r lifecycle::badge("stable")`+ logger::log_trace("TealDatasetConnector$set_keys keys set for dataset: { deparse1(self$get_dataname()) }.") |
||
4 | +274 |
- #'+ |
||
5 | -+ | |||
275 | +182x |
- #' @details The information summary provides row numbers and number of duplicates+ return(invisible(self)) |
||
6 | +276 |
- #' for each duplicated key value.+ }, |
||
7 | +277 |
- #'+ |
||
8 | +278 |
- #' @param dataset `TealDataset` or `data.frame` a dataset, which will be tested+ # ___ pull ==== |
||
9 | +279 |
- #' @param keys `character` vector of variable names in `dataset` consisting the key+ #' @description |
||
10 | +280 |
- #' or `keys` object, which does have a `primary` element with a vector of variable+ #' Pull the data (and metadata if it is a `Callable`) |
||
11 | +281 |
- #' names in `dataset` consisting the key. Optional, default: NULL+ #' |
||
12 | +282 |
- #'+ #' Read or create data using `pull_callable` specified in the constructor. |
||
13 | +283 |
- #' @return a `tibble` with variables consisting the key and `row_no` and `duplicates_count` columns+ #' |
||
14 | +284 |
- #'+ #' @param args (`NULL` or named `list`)\cr |
||
15 | +285 |
- #' @note Raises an exception when this function cannot determine the primary key columns of the tested object.+ #' additional dynamic arguments for pull function. `args` can be omitted if `pull_callable` |
||
16 | +286 |
- #'+ #' from constructor already contains all necessary arguments to pull data. One can try |
||
17 | +287 |
- #' @examples+ #' to execute `pull_callable` directly by `x$pull_callable$run()` or to get code using |
||
18 | +288 |
- #'+ #' `x$pull_callable$get_code()`. `args` specified in pull are used temporary to get data but |
||
19 | +289 |
- #' adsl <- teal.data::example_cdisc_data("ADSL")+ #' not saved in code. |
||
20 | +290 |
- #' # create a TealDataset with default keys+ #' @param try (`logical` value)\cr |
||
21 | +291 |
- #' rel_adsl <- cdisc_dataset("ADSL", adsl)+ #' whether perform function evaluation inside `try` clause |
||
22 | +292 |
- #' get_key_duplicates(rel_adsl)+ #' |
||
23 | +293 |
- #'+ #' @return (`self`) if successful. |
||
24 | +294 |
- #' df <- as.data.frame(+ pull = function(args = NULL, try = FALSE) { |
||
25 | -+ | |||
295 | +118x |
- #' list(a = c("a", "a", "b", "b", "c"), b = c(1, 2, 3, 3, 4), c = c(1, 2, 3, 4, 5))+ logger::log_trace("TealDatasetConnector$pull pulling dataset: {self$get_dataname() }.") |
||
26 | -+ | |||
296 | +118x |
- #' )+ data <- private$pull_internal(args = args, try = try) |
||
27 | -+ | |||
297 | +116x |
- #' res <- get_key_duplicates(df, keys = c("a", "b")) # duplicated keys are in rows 3 and 4+ if (!self$is_failed()) { |
||
28 | +298 |
- #' print(res) # prints a tibble+ # The first time object is pulled, private$dataset may be NULL if mutate method was never called |
||
29 | -+ | |||
299 | +115x |
- #' \dontrun{+ has_dataset <- !is.null(private$dataset) |
||
30 | -+ | |||
300 | +115x |
- #' get_key_duplicates(df) # raises an exception, because keys are missing with no default+ if (has_dataset) { |
||
31 | -+ | |||
301 | +13x |
- #' }+ code_in_dataset <- private$dataset$get_code_class(nodeps = TRUE) |
||
32 | -+ | |||
302 | +13x |
- #'+ vars_in_dataset <- private$dataset$get_vars() |
||
33 | +303 |
- #' @export+ } |
||
34 | +304 |
- get_key_duplicates <- function(dataset, keys = NULL) {+ |
||
35 | -46x | -
- UseMethod("get_key_duplicates", dataset)- |
- ||
36 | -- |
- }- |
- ||
37 | -+ | 305 | +115x |
-
+ pulled_metadata <- private$pull_metadata_internal() |
38 | -+ | |||
306 | +115x |
- #' @rdname get_key_duplicates+ private$dataset <- dataset( |
||
39 | -+ | |||
307 | +115x |
- #' @export+ dataname = self$get_dataname(), |
||
40 | -+ | |||
308 | +115x |
- get_key_duplicates.TealDataset <- function(dataset, keys = NULL) { # nolint+ x = data, |
||
41 | -! | +|||
309 | +115x |
- df <- get_raw_data(dataset)+ keys = character(0), # keys need to be set after mutate |
||
42 | -! | +|||
310 | +115x |
- if (is.null(keys)) {+ label = self$get_dataset_label(), |
||
43 | -! | +|||
311 | +115x |
- keys_ds <- get_keys(dataset)+ code = private$get_pull_code_class(), |
||
44 | -! | +|||
312 | +115x |
- keys <- if (is.null(keys_ds)) character(0) else keys_ds+ metadata = pulled_metadata |
||
45 | +313 |
- }+ ) |
||
46 | +314 | |||
47 | -! | +|||
315 | +115x |
- get_key_duplicates_util(df, keys)+ if (has_dataset) { |
||
48 | -+ | |||
316 | +13x |
- }+ private$dataset$mutate( |
||
49 | -+ | |||
317 | +13x |
-
+ code = code_in_dataset, |
||
50 | -+ | |||
318 | +13x |
- #' @rdname get_key_duplicates+ vars = vars_in_dataset |
||
51 | +319 |
- #' @export+ ) |
||
52 | +320 |
- get_key_duplicates.data.frame <- function(dataset, keys = NULL) { # nolint+ } |
||
53 | -46x | +321 | +115x |
- if (is.null(keys)) {+ set_keys(private$dataset, self$get_keys()) |
54 | -! | +|||
322 | +115x |
- attr_key <- attr(dataset, "primary_key")+ private$is_pulled_flag <- TRUE |
||
55 | -! | +|||
323 | +115x |
- keys <- if (is.null(attr_key)) character(0) else attr+ logger::log_trace("TealDatasetConnector$pull pulled dataset: {self$get_dataname() }.") |
||
56 | +324 |
- }+ } else { |
||
57 | -46x | +325 | +1x |
- get_key_duplicates_util(dataset, keys)+ logger::log_error("TealDatasetConnector$pull failed to pull dataset: {self$get_dataname() }.") |
58 | +326 |
- }+ } |
||
59 | +327 | |||
60 | -+ | |||
328 | +116x |
- #' Creates a duplicate keys information summary.+ return(invisible(self)) |
||
61 | +329 |
- #'+ }, |
||
62 | +330 |
- #' @details+ #' @description |
||
63 | +331 |
- #' Accepts a list of variable names - `keys`, which are treated as the+ #' Set arguments to the pulling function |
||
64 | +332 |
- #' key to the `data.frame` argument. An instance of duplicated key is+ #' |
||
65 | +333 |
- #' defined as two rows, which have the same values in columns defined by `keys`.+ #' @param args (`NULL` or named `list`) dynamic arguments to function |
||
66 | +334 |
- #' Per each key value with duplicates returns a row in a `tibble`. The return table+ #' |
||
67 | +335 |
- #' has columns corresponding to the variable names passed in `keys` and+ #' @return (`self`) invisibly for chaining |
||
68 | +336 |
- #' two additional columns: `rows` and `n`, which provide+ set_args = function(args) {+ |
+ ||
337 | +1x | +
+ set_args(private$pull_callable, args)+ |
+ ||
338 | +1x | +
+ logger::log_trace("TealDatasetConnector$set_args pull args set for dataset: {self$get_dataname() }.")+ |
+ ||
339 | +1x | +
+ return(invisible(self)) |
||
69 | +340 |
- #' information about row numbers of the original dataframe, which contain duplicated keys+ }, |
||
70 | +341 |
- #' and total duplicates counts.+ |
||
71 | +342 |
- #'+ # ___ mutate ==== |
||
72 | +343 |
- #' @param dataframe dataframe+ #' @description |
||
73 | +344 |
- #' @param keys `character` vector of variable names consisting the key to the `data.frame`+ #' Dispatcher for either eager or delayed mutate methods |
||
74 | +345 |
- #'+ #' |
||
75 | +346 |
- #' @return `data.frame` with a duplicate keys information summary+ #' Either code or script must be provided, but not both. |
||
76 | +347 |
- #'+ #' |
||
77 | +348 |
- #' @keywords internal+ #' @return (`self`) invisibly for chaining. |
||
78 | +349 |
- #'+ mutate = function(code, vars = list()) { |
||
79 | -+ | |||
350 | +46x |
- #' @examples+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
80 | +351 |
- #' df <- data.frame(+ |
||
81 | -+ | |||
352 | +46x |
- #' a = c("a", "a", "b", "b", "c"),+ if (is.null(private$dataset)) { |
||
82 | +353 |
- #' b = c(1, 2, 3, 3, 4),+ # just needs a dummy TealDataset object to store mutate code, hence col = 1 |
||
83 | -+ | |||
354 | +11x |
- #' c = c(1, 2, 3, 4, 5)+ private$dataset <- TealDataset$new(dataname = self$get_dataname(), x = data.frame(col = 1)) |
||
84 | +355 |
- #' )+ } |
||
85 | -+ | |||
356 | +46x |
- #' res <- teal.data:::get_key_duplicates_util(df, keys = c("a", "b"))+ private$dataset$mutate(code = code, vars = vars, force_delay = !self$is_pulled()) |
||
86 | +357 |
- #' print(res) # duplicated keys are in rows 3 and 4+ # should be called at the end so that failure in TealDataset object will prevent it. |
||
87 | -+ | |||
358 | +45x |
- #' @seealso [get_key_duplicates]+ private$set_var_r6(vars) |
||
88 | -+ | |||
359 | +45x |
- get_key_duplicates_util <- function(dataframe, keys) {+ logger::log_trace( |
||
89 | -53x | +360 | +45x |
- checkmate::assert_data_frame(dataframe)+ sprintf( |
90 | -52x | +361 | +45x |
- checkmate::assert_character(keys)+ "TealDatasetConnector$mutate mutated dataset '%s' using the code (%s lines) and vars (%s).", |
91 | -50x | +362 | +45x |
- stopifnot(+ self$get_dataname(), |
92 | -50x | +363 | +45x |
- all(+ length(parse(text = if (inherits(code, "CodeClass")) code$get_code() else code)), |
93 | -50x | +364 | +45x |
- vapply(keys, FUN.VALUE = logical(1), FUN = function(key) key %in% colnames(dataframe))+ paste(names(vars), collapse = ", ") |
94 | +365 |
- )+ ) |
||
95 | +366 |
- )+ ) |
||
96 | +367 | |||
97 | +368 |
- # The goal is to print values of duplicated primary keys with number of duplicates and row numbers+ |
||
98 | -49x | +369 | +45x |
- duplicates <- dataframe[, keys, drop = FALSE]+ return(invisible(self)) |
99 | -49x | +|||
370 | +
- duplicates$dups <- duplicated(duplicates, fromLast = FALSE) | duplicated(duplicates, fromLast = TRUE)+ }, |
|||
100 | -49x | +|||
371 | +
- duplicates$row_number <- seq_len(nrow(duplicates))+ |
|||
101 | -49x | +|||
372 | +
- duplicates <- duplicates[duplicates$dups, ]+ # ___ status ==== |
|||
102 | -49x | +|||
373 | +
- duplicates$dups <- NULL+ #' @description |
|||
103 | +374 |
-
+ #' Check if pull has not failed. |
||
104 | -49x | +|||
375 | +
- if (nrow(duplicates) == 0) {+ #' |
|||
105 | -45x | +|||
376 | +
- duplicates$rows <- character(0)+ #' @return `TRUE` if pull failed, else `FALSE` |
|||
106 | -45x | +|||
377 | +
- duplicates$row_number <- NULL+ is_failed = function() { |
|||
107 | -45x | +378 | +143x |
- duplicates$n <- integer(0)+ return(private$pull_callable$is_failed()) |
108 | -45x | +|||
379 | +
- return(duplicates)+ }, |
|||
109 | +380 |
- }+ #' @description |
||
110 | +381 |
-
+ #' Check if dataset has already been pulled. |
||
111 | -4x | +|||
382 | +
- groups <- split(duplicates, duplicates[, keys, drop = FALSE], drop = TRUE)+ #' |
|||
112 | -4x | +|||
383 | +
- summary_list <- lapply(groups, function(group) {+ #' @return `TRUE` if connector has been already pulled, else `FALSE` |
|||
113 | -6x | +|||
384 | +
- ans <- group[1, keys, drop = FALSE]+ is_pulled = function() { |
|||
114 | -6x | +385 | +680x |
- ans$rows <- paste(group[, "row_number"], collapse = ",")+ private$is_pulled_flag |
115 | -6x | +|||
386 | +
- ans$n <- nrow(group)+ }, |
|||
116 | -6x | +|||
387 | +
- ans+ #' @description |
|||
117 | +388 |
- })+ #' Check if dataset has mutations that are delayed |
||
118 | -4x | +|||
389 | +
- summary <- do.call(rbind, summary_list)+ #'+ |
+ |||
390 | ++ |
+ #' @return `logical`+ |
+ ||
391 | ++ |
+ is_mutate_delayed = function() { |
||
119 | -4x | +392 | +58x |
- rownames(summary) <- NULL+ if (is.null(private$dataset)) { |
120 | -4x | +393 | +2x |
- summary+ FALSE |
121 | +394 |
- }+ } else { |
1 | -+ | |||
395 | +56x |
- #' Create a new `TealDatasetConnector` object+ private$dataset$is_mutate_delayed() |
||
2 | +396 |
- #'+ } |
||
3 | +397 |
- #' `r lifecycle::badge("stable")`+ }, |
||
4 | +398 |
- #'+ |
||
5 | +399 |
- #' Create `TealDatasetConnector` from [callable_function].+ # ___ check ==== |
||
6 | +400 |
- #'+ #' @description |
||
7 | +401 |
- #' @param dataname (`character`)\cr+ #' Check to determine if the raw data is reproducible from the |
||
8 | +402 |
- #' A given name for the dataset it may not contain spaces+ #' `get_code()` code. |
||
9 | +403 |
- #'+ #' @return |
||
10 | +404 |
- #' @param pull_callable (`CallableFunction`)\cr+ #' `TRUE` always for all connectors to avoid evaluating the same code multiple times. |
||
11 | +405 |
- #' function with necessary arguments set to fetch data from connection.+ check = function() { |
||
12 | -+ | |||
406 | +10x |
- #'+ return(TRUE) |
||
13 | +407 |
- #' @param keys optional, (`character`)\cr+ }, |
||
14 | +408 |
- #' vector of dataset primary keys column names+ # ___ shiny ==== |
||
15 | +409 |
- #'+ #' @description |
||
16 | +410 |
- #' @param label (`character`)\cr+ #' Sets the shiny UI according to the given inputs. |
||
17 | +411 |
- #' Label to describe the dataset.+ #' Inputs must provide only scalar (length of 1) variables. |
||
18 | +412 |
- #'+ #' @param inputs (`function`) A shiny module UI function with single argument `ns`. |
||
19 | +413 |
- #' @param code (`character`)\cr+ #' This function needs to return a list of shiny inputs with their `inputId` wrapped |
||
20 | +414 |
- #' A character string defining code to modify `raw_data` from this dataset. To modify+ #' in function `ns`. The `inputId` must match exactly the argument name to be set. |
||
21 | +415 |
- #' current dataset code should contain at least one assignment to object defined in `dataname`+ #' See example. |
||
22 | +416 |
- #' argument. For example if `dataname = ADSL` example code should contain+ #' Nested lists are not allowed. |
||
23 | +417 |
- #' `ADSL <- <some R code>`. Can't be used simultaneously with `script`+ #' @return (`self`) invisibly for chaining. |
||
24 | +418 |
- #'+ #' @examples |
||
25 | +419 |
- #' @param script (`character`)\cr+ #' ds <- dataset_connector("xyz", pull_callable = callable_function(data.frame)) |
||
26 | +420 |
- #' Alternatively to `code` - location of the file containing modification code.+ #' ds$set_ui_input( |
||
27 | +421 |
- #' Can't be used simultaneously with `script`.+ #' function(ns) { |
||
28 | +422 |
- #'+ #' list(sliderInput(ns("colA"), "Select value for colA", min = 0, max = 10, value = 3), |
||
29 | +423 |
- #' @param vars (named `list`)) \cr+ #' sliderInput(ns("colB"), "Select value for colB", min = 0, max = 10, value = 7)) |
||
30 | +424 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ #' } |
||
31 | +425 |
- #' other constant value, this/these object(s) should be included as named+ #' ) |
||
32 | +426 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ #' \dontrun{ |
||
33 | +427 |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ #' ds$launch() |
||
34 | +428 |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ #' } |
||
35 | +429 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ set_ui_input = function(inputs = NULL) { |
||
36 | -+ | |||
430 | +3x |
- #' are included to this object as local `vars` and they cannot be modified+ stopifnot(is.null(inputs) || is.function(inputs)) |
||
37 | -+ | |||
431 | +3x |
- #' within another dataset.+ if (is.function(inputs)) {+ |
+ ||
432 | +3x | +
+ if (!identical(names(formals(inputs)), "ns")) {+ |
+ ||
433 | +! | +
+ stop("'inputs' must be a function of a single argument called 'ns'") |
||
38 | +434 |
- #'+ } |
||
39 | +435 |
- #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr+ } |
||
40 | -+ | |||
436 | +3x |
- #' Field containing either the metadata about the dataset (each element of the list+ private$ui_input <- inputs |
||
41 | -+ | |||
437 | +3x |
- #' should be atomic and length one) or a `CallableFuntion` to pull the metadata+ logger::log_trace( |
||
42 | -+ | |||
438 | +3x |
- #' from a connection. This should return a `list` or an object which can be+ "TealDatasetConnector$set_ui_input ui_input set for dataset: { deparse1(self$get_dataname()) }." |
||
43 | +439 |
- #' converted to a list with `as.list`.+ ) |
||
44 | -+ | |||
440 | +3x |
- #' @return new `TealDatasetConnector` object+ return(invisible(self)) |
||
45 | +441 |
- #'+ }, |
||
46 | +442 |
- #' @examples+ #' @description |
||
47 | +443 |
- #' library(MultiAssayExperiment)+ #' Get shiny `ui` function |
||
48 | +444 |
- #' # data.frame example+ #' @param id (`character`) namespace id |
||
49 | +445 |
- #' pull_fun2 <- callable_function(data.frame)+ #' @return shiny UI in given namespace id |
||
50 | +446 |
- #' pull_fun2$set_args(args = list(a = c(1, 2, 3)))+ get_ui = function(id) { |
||
51 | -+ | |||
447 | +2x |
- #' dataset_connector("test", pull_fun2)+ checkmate::assert_string(id) |
||
52 | -+ | |||
448 | +2x |
- #'+ if (!is.null(private$ui)) { |
||
53 | -+ | |||
449 | +2x |
- #' # MultiAssayExperiment example+ private$ui(id) |
||
54 | +450 |
- #' pull_fun <- callable_function(+ } |
||
55 | +451 |
- #' function() {+ }, |
||
56 | +452 |
- #' library("MultiAssayExperiment")+ #' @description |
||
57 | +453 |
- #' data("miniACC")+ #' Get shiny server function |
||
58 | +454 |
- #' return(miniACC)+ #' @return shiny server function |
||
59 | +455 |
- #' }+ get_server = function() { |
||
60 | -+ | |||
456 | +! |
- #' )+ return(private$server) |
||
61 | +457 |
- #' dataset_connector(+ }, |
||
62 | +458 |
- #' "miniacc",+ #' @description |
||
63 | +459 |
- #' pull_fun,+ #' Launches a shiny app. |
||
64 | +460 |
- #' code = 'library("MultiAssayExperiment"); data("miniACC"); return(miniACC)'+ #' @return Shiny app |
||
65 | +461 |
- #' )+ #' @examples |
||
66 | +462 |
- #' @export+ #' ds <- dataset_connector("xyz", pull_callable = callable_function(data.frame)) |
||
67 | +463 |
- dataset_connector <- function(dataname,+ #' ds$set_ui_input( |
||
68 | +464 |
- pull_callable,+ #' function(ns) { |
||
69 | +465 |
- keys = character(0),+ #' list(sliderInput(ns("colA"), "Select value for colA", min = 0, max = 10, value = 3), |
||
70 | +466 |
- label = character(0),+ #' sliderInput(ns("colB"), "Select value for colB", min = 0, max = 10, value = 7)) |
||
71 | +467 |
- code = character(0),+ #' } |
||
72 | +468 |
- script = character(0),+ #' ) |
||
73 | +469 |
- vars = list(),+ #' \dontrun{ |
||
74 | +470 |
- metadata = NULL) {- |
- ||
75 | -112x | -
- checkmate::assert_string(dataname)+ #' ds$launch() |
||
76 | -111x | +|||
471 | +
- stopifnot(inherits(pull_callable, "Callable"))+ #' } |
|||
77 | -111x | +|||
472 | +
- checkmate::assert_character(keys, any.missing = FALSE)+ launch = function() { |
|||
78 | -111x | +|||
473 | +! |
- checkmate::assert_character(code, any.missing = FALSE)+ if (is.null(private$server)) { |
||
79 | -111x | +|||
474 | +! |
- checkmate::assert_character(label, any.missing = FALSE)+ stop("No arguments set yet. Please use set_ui_input method first.") |
||
80 | +475 | - - | -||
81 | -111x | -
- if (!checkmate::test_class(metadata, "Callable", null.ok = TRUE)) {+ } |
||
82 | -14x | +|||
476 | +! |
- validate_metadata(metadata)+ shinyApp( |
||
83 | -+ | |||
477 | +! |
- }+ ui = fluidPage( |
||
84 | -+ | |||
478 | +! |
-
+ theme = get_teal_bs_theme(), |
||
85 | -111x | +|||
479 | +! |
- x <- TealDatasetConnector$new(+ self$get_ui(id = "main_app"), |
||
86 | -111x | +|||
480 | +! |
- dataname = dataname,+ shinyjs::useShinyjs(), |
||
87 | -111x | +|||
481 | +! |
- pull_callable = pull_callable,+ br(), |
||
88 | -111x | +|||
482 | +! |
- keys = keys,+ actionButton("pull", "Get data"), |
||
89 | -111x | +|||
483 | +! |
- code = code_from_script(code, script),+ br(), |
||
90 | -111x | +|||
484 | +! |
- label = label,+ tableOutput("result") |
||
91 | -111x | +|||
485 | +
- vars = vars,+ ), |
|||
92 | -111x | +|||
486 | +! |
- metadata = metadata+ server = function(input, output, session) { |
||
93 | -+ | |||
487 | +! |
- )+ session$onSessionEnded(stopApp) |
||
94 | -+ | |||
488 | +! |
-
+ observeEvent(input$pull, { |
||
95 | -111x | +|||
489 | +! |
- return(x)+ self$get_server()(id = "main_app") |
||
96 | -+ | |||
490 | +! |
- }+ if (self$is_pulled()) { |
||
97 | -+ | |||
491 | +! |
-
+ output$result <- renderTable(head(self$get_raw_data())) |
||
98 | +492 |
- #' Create a new `CDISCTealDatasetConnector` object+ } |
||
99 | +493 |
- #'+ }) |
||
100 | +494 |
- #' `r lifecycle::badge("stable")`+ } |
||
101 | +495 |
- #'+ ) |
||
102 | +496 |
- #' Create `CDISCTealDatasetConnector` from [callable_function].+ } |
||
103 | +497 |
- #'+ ), |
||
104 | +498 |
- #' @inheritParams dataset_connector+ ## __Private Fields ==== |
||
105 | +499 |
- #' @inheritParams cdisc_dataset+ private = list( |
||
106 | +500 |
- #'+ dataset = NULL, # TealDataset |
||
107 | +501 |
- #' @return new `CDISCTealDatasetConnector` object+ pull_callable = NULL, # Callable |
||
108 | +502 |
- #'+ pull_vars = list(), # named list |
||
109 | +503 |
- #' @export+ dataname = character(0), |
||
110 | +504 |
- cdisc_dataset_connector <- function(dataname,+ dataset_label = character(0), |
||
111 | +505 |
- pull_callable,+ metadata = NULL, # Callable or list |
||
112 | +506 |
- keys,+ keys = NULL, |
||
113 | +507 |
- parent = `if`(identical(dataname, "ADSL"), character(0), "ADSL"),+ var_r6 = list(), |
||
114 | +508 |
- label = character(0),+ ui_input = NULL, # NULL or list |
||
115 | +509 |
- code = character(0),+ is_pulled_flag = FALSE, |
||
116 | +510 |
- script = character(0),+ |
||
117 | +511 |
- vars = list(),+ ## __Private Methods ==== |
||
118 | +512 |
- metadata = NULL) {+ ui = function(id) { |
||
119 | -32x | +513 | +2x |
- checkmate::assert_string(dataname)+ ns <- NS(id) |
120 | -32x | +|||
514 | +
- stopifnot(inherits(pull_callable, "Callable"))+ # add namespace to input ids |
|||
121 | -32x | +515 | +2x |
- checkmate::assert_character(keys, any.missing = FALSE)+ ui <- if (!is.null(private$ui_input)) { |
122 | -32x | +516 | +1x |
- checkmate::assert_character(parent, max.len = 1, any.missing = FALSE)+ do.call(private$ui_input, list(ns = ns)) |
123 | -32x | +|||
517 | +
- checkmate::assert_character(code, max.len = 1, any.missing = FALSE)+ } else { |
|||
124 | -32x | +518 | +1x |
- checkmate::assert_character(label, max.len = 1, any.missing = FALSE)+ NULL |
125 | +519 |
-
+ } |
||
126 | -32x | +|||
520 | +
- if (!checkmate::test_class(metadata, "Callable", null.ok = TRUE)) {+ # check ui inputs |
|||
127 | -11x | -
- validate_metadata(metadata)- |
- ||
128 | -+ | 521 | +2x |
- }+ if (!is.null(ui)) { |
129 | -+ | |||
522 | +1x |
-
+ checkmate::assert_list(ui, types = "shiny.tag") |
||
130 | -32x | +523 | +1x |
- x <- CDISCTealDatasetConnector$new(+ attr_class <- vapply(lapply(ui, "[[", i = "attribs"), "[[", character(1), i = "class") |
131 | -32x | +524 | +1x |
- dataname = dataname,+ if (!all(grepl("shiny-input-container", attr_class))) { |
132 | -32x | +|||
525 | +! |
- pull_callable = pull_callable,+ stop("All elements must be shiny inputs") |
||
133 | -32x | +|||
526 | +
- keys = keys,+ } |
|||
134 | -32x | +|||
527 | +
- parent = parent,+ } |
|||
135 | -32x | +|||
528 | +
- code = code_from_script(code, script),+ # create ui |
|||
136 | -32x | +529 | +2x |
- label = label,+ if (!is.null(ui)) { |
137 | -32x | +530 | +1x |
- vars = vars,+ tags$div( |
138 | -32x | +531 | +1x |
- metadata = metadata+ tags$div( |
139 | -+ | |||
532 | +1x |
- )+ id = ns("inputs"), |
||
140 | -+ | |||
533 | +1x |
-
+ h4("TealDataset Connector for ", code(self$get_dataname())), |
||
141 | -32x | +534 | +1x |
- return(x)+ ui |
142 | +535 |
- }+ ) |
||
143 | +536 |
-
+ ) |
||
144 | +537 |
-
+ } |
||
145 | +538 |
- #' Load `TealDatasetConnector` object from a file+ }, |
||
146 | +539 |
- #'+ server = function(id, data_args = NULL) { |
||
147 | -+ | |||
540 | +! |
- #' `r lifecycle::badge("stable")`+ moduleServer( |
||
148 | -+ | |||
541 | +! |
- #'+ id = id, |
||
149 | -+ | |||
542 | +! |
- #' Please note that the script has to end with a call creating desired object. The error will+ function(input, output, session) { |
||
150 | -+ | |||
543 | +! |
- #' be raised otherwise.+ withProgress(value = 1, message = paste("Pulling", self$get_dataname()), { |
||
151 | +544 |
- #'+ # set args to save them - args set will be returned in the call |
||
152 | -+ | |||
545 | +! |
- #' @inheritParams dataset_file+ dataset_args <- if (!is.null(private$ui_input)) { |
||
153 | -+ | |||
546 | +! |
- #'+ reactiveValuesToList(input) |
||
154 | +547 |
- #' @return `TealDatasetConnector` object+ } else { |
||
155 | -+ | |||
548 | +! |
- #'+ NULL |
||
156 | +549 |
- #' @rdname dataset_connector_file+ } |
||
157 | -+ | |||
550 | +! |
- #'+ if (length(dataset_args) > 0) { |
||
158 | -+ | |||
551 | +! |
- #' @export+ self$set_args(args = dataset_args) |
||
159 | +552 |
- #'+ } |
||
160 | +553 |
- #' @examples+ |
||
161 | -+ | |||
554 | +! |
- #' # simple example+ self$pull(args = data_args, try = TRUE) |
||
162 | +555 |
- #' library(magrittr)+ |
||
163 | +556 |
- #' file_example <- tempfile(fileext = ".R")+ # print error if any |
||
164 | +557 |
- #' writeLines(+ # error doesn't break an app |
||
165 | -+ | |||
558 | +! |
- #' text = c(+ if (self$is_failed()) { |
||
166 | -+ | |||
559 | +! |
- #' "library(teal.data)+ shinyjs::alert( |
||
167 | -+ | |||
560 | +! |
- #'+ sprintf( |
||
168 | -+ | |||
561 | +! |
- #' pull_callable <- callable_function(teal.data::example_cdisc_data) %>%+ "Error pulling %s:\nError message: %s", |
||
169 | -+ | |||
562 | +! |
- #' set_args(list(dataname = \"ADSL\"))+ self$get_dataname(), |
||
170 | -+ | |||
563 | +! |
- #' dataset_connector(\"ADSL\", pull_callable, get_cdisc_keys(\"ADSL\"))"+ self$get_error_message() |
||
171 | +564 |
- #' ),+ ) |
||
172 | +565 |
- #' con = file_example+ ) |
||
173 | +566 |
- #' )+ } |
||
174 | +567 |
- #' x <- dataset_connector_file(file_example)+ }) |
||
175 | +568 |
- #' get_code(x)+ } |
||
176 | +569 |
- dataset_connector_file <- function(path) { # nolint- |
- ||
177 | -! | -
- object <- object_file(path, "TealDatasetConnector")+ ) |
||
178 | +570 | ! |
- return(object)+ return(invisible(self)) |
|
179 | +571 |
- }+ }, |
||
180 | +572 | |||
181 | +573 |
- #' Load `CDISCTealDatasetConnector` object from a file+ # need to have a custom deep_clone because one of the key fields are reference-type object |
||
182 | +574 |
- #'+ # in particular: dataset is a R6 object that wouldn't be cloned using default clone(deep = T) |
||
183 | +575 |
- #' `r lifecycle::badge("stable")`+ deep_clone = function(name, value) { |
||
184 | -+ | |||
576 | +208x |
- #'+ deep_clone_r6(name, value) |
||
185 | +577 |
- #' Please note that the script has to end with a call creating desired object. The error will+ }, |
||
186 | +578 |
- #' be raised otherwise.+ get_pull_code_class = function(args = NULL) { |
||
187 | -+ | |||
579 | +301x |
- #'+ res <- CodeClass$new() |
||
188 | -+ | |||
580 | +301x |
- #' @inheritParams dataset_connector_file+ res$append(list_to_code_class(private$pull_vars)) |
||
189 | -+ | |||
581 | +301x |
- #'+ code <- if (inherits(private$pull_callable, "CallableCode")) { |
||
190 | -+ | |||
582 | +21x |
- #' @return `CDISCTealDatasetConnector` object+ tmp <- private$pull_callable$get_call(deparse = FALSE) |
||
191 | -+ | |||
583 | +21x |
- #'+ tmp[[length(tmp)]] <- substitute(a <- b, list(a = as.name(private$dataname), b = tmp[[length(tmp)]])) |
||
192 | -+ | |||
584 | +21x |
- #' @rdname dataset_connector_file+ paste0(vapply(tmp, deparse1, character(1), collapse = "\n"), collapse = "\n") |
||
193 | +585 |
- #'+ } else { |
||
194 | -+ | |||
586 | +280x |
- #' @export+ deparse1(substitute( |
||
195 | -+ | |||
587 | +280x |
- #'+ a <- b, |
||
196 | -+ | |||
588 | +280x |
- #' @examples+ list( |
||
197 | -+ | |||
589 | +280x |
- #' # simple example+ a = as.name(private$dataname), |
||
198 | -+ | |||
590 | +280x |
- #' library(magrittr)+ b = private$pull_callable$get_call(deparse = FALSE, args = args) |
||
199 | +591 |
- #' file_example <- tempfile(fileext = ".R")+ ) |
||
200 | -+ | |||
592 | +280x |
- #' writeLines(+ ), collapse = "\n") |
||
201 | +593 |
- #' text = c(+ } |
||
202 | +594 |
- #' "library(teal.data)+ |
||
203 | -+ | |||
595 | +301x |
- #'+ res$set_code(code = code, dataname = private$dataname, deps = names(private$pull_vars)) |
||
204 | -+ | |||
596 | +301x |
- #' pull_callable <- callable_function(teal.data::example_cdisc_data) %>%+ return(res) |
||
205 | +597 |
- #' set_args(list(dataname = \"ADSL\"))+ }, |
||
206 | +598 |
- #' cdisc_dataset_connector(\"ADSL\", pull_callable, get_cdisc_keys(\"ADSL\"))"+ set_pull_callable = function(pull_callable) { |
||
207 | -+ | |||
599 | +182x |
- #' ),+ stopifnot(inherits(pull_callable, "Callable")) |
||
208 | -+ | |||
600 | +182x |
- #' con = file_example+ private$pull_callable <- pull_callable |
||
209 | -+ | |||
601 | +182x |
- #' )+ return(invisible(self)) |
||
210 | +602 |
- #' x <- cdisc_dataset_connector_file(file_example)+ }, |
||
211 | +603 |
- #' get_code(x)+ set_metadata = function(metadata) {+ |
+ ||
604 | +182x | +
+ if (inherits(metadata, "Callable")) { |
||
212 | -+ | |||
605 | +4x |
- cdisc_dataset_connector_file <- function(path) { # nolint+ private$metadata <- metadata |
||
213 | -! | +|||
606 | +
- object <- object_file(path, "CDISCTealDatasetConnector")+ } else { |
|||
214 | -! | +|||
607 | +178x |
- return(object)+ validate_metadata(metadata) |
||
215 | -+ | |||
608 | +178x |
- }+ private$metadata <- metadata |
||
216 | +609 |
-
+ } |
||
217 | -+ | |||
610 | +182x |
- # RDS ====+ return(invisible(self)) |
||
218 | +611 |
- #' `RDS` `TealDatasetConnector`+ }, |
||
219 | +612 |
- #'+ set_pull_vars = function(pull_vars) { |
||
220 | -+ | |||
613 | +182x |
- #' `r lifecycle::badge("stable")`+ checkmate::assert_list(pull_vars, min.len = 0, names = "unique") |
||
221 | -+ | |||
614 | +182x |
- #'+ private$pull_vars <- pull_vars |
||
222 | -+ | |||
615 | +182x |
- #' Create a `TealDatasetConnector` from `RDS` file.+ return(invisible(self)) |
||
223 | +616 |
- #'+ }, |
||
224 | +617 |
- #' @inheritParams dataset_connector+ pull_metadata_internal = function() { |
||
225 | -+ | |||
618 | +115x |
- #' @inheritParams fun_dataset_connector+ if (!checkmate::test_class(private$metadata, "Callable")) { |
||
226 | -+ | |||
619 | +112x |
- #' @param file (`character`)\cr+ return(private$metadata) |
||
227 | +620 |
- #' path to (`.rds` or `.R`) that contains `data.frame` object or+ } |
||
228 | +621 |
- #' code to `source`+ |
||
229 | -+ | |||
622 | +3x |
- #'+ logger::log_trace("TealDatasetConnector$pull pulling metadata for dataset: {self$get_dataname() }.") |
||
230 | -+ | |||
623 | +3x |
- #' @param ... (`optional`)\cr+ pulled_metadata <- private$metadata$run(try = TRUE) |
||
231 | +624 |
- #' additional arguments applied to [base::readRDS()] function+ |
||
232 | -+ | |||
625 | +3x |
- #'+ if (checkmate::test_class(pulled_metadata, c("simpleError", "error"))) { |
||
233 | -+ | |||
626 | +1x |
- #' @export+ logger::log_warn("TealDatasetConnector$pull pulling metadata failed for dataset: {self$get_dataname() }.") |
||
234 | -+ | |||
627 | +1x |
- #'+ return(NULL) |
||
235 | +628 |
- #' @rdname rds_dataset_connector+ } |
||
236 | +629 |
- #'+ |
||
237 | +630 |
- #' @examples+ # metadata pulled, now lets make sure it is valid |
||
238 | -+ | |||
631 | +2x |
- #' \dontrun{+ tryCatch( |
||
239 | +632 |
- #' x <- rds_dataset_connector(+ { |
||
240 | -+ | |||
633 | +2x |
- #' dataname = "ADSL",+ pulled_metadata <- as.list(pulled_metadata) |
||
241 | -+ | |||
634 | +2x |
- #' file = "path/to/file.RDS"+ validate_metadata(pulled_metadata) |
||
242 | -+ | |||
635 | +1x |
- #' )+ logger::log_trace("TealDatasetConnector$pull pulled metadata for dataset: {self$get_dataname() }.") |
||
243 | -+ | |||
636 | +1x |
- #' x$get_code()+ return(pulled_metadata) |
||
244 | +637 |
- #' }+ }, |
||
245 | -+ | |||
638 | +2x |
- rds_dataset_connector <- function(dataname,+ error = function(e) { |
||
246 | -+ | |||
639 | +1x |
- file,+ logger::log_warn("TealDatasetConnector$pull invalid metadata for dataset: {self$get_dataname() }.") |
||
247 | -+ | |||
640 | +1x |
- keys = character(0),+ return(NULL) |
||
248 | +641 |
- label = character(0),+ } |
||
249 | +642 |
- code = character(0),+ ) |
||
250 | +643 |
- script = character(0),+ }, |
||
251 | +644 |
- metadata = list(type = "rds", file = file),+ pull_internal = function(args = NULL, try = FALSE) { |
||
252 | +645 |
- ...) {+ # include objects CallableFunction environment |
||
253 | -4x | +646 | +118x |
- dot_args <- list(...)+ if (length(private$pull_vars) > 0) { |
254 | -4x | +647 | +57x |
- checkmate::assert_list(dot_args, min.len = 0, names = "unique")+ for (var_idx in seq_along(private$pull_vars)) { |
255 | -4x | +648 | +57x |
- checkmate::assert_string(file)+ var_name <- names(private$pull_vars)[[var_idx]] |
256 | -4x | +649 | +57x |
- if (!file.exists(file)) {+ var_value <- private$pull_vars[[var_idx]] |
257 | -1x | +|||
650 | +
- stop("File ", file, " does not exist.", call. = FALSE)+ |
|||
258 | +651 |
- }+ # assignment is done in pull_callable only once |
||
259 | +652 |
-
+ # because x is locked within local environment |
||
260 | -3x | +|||
653 | +
- x_fun <- callable_function(readRDS) # nolint+ # this means that re-assignment is not possible and will be silently skipped |
|||
261 | -3x | +|||
654 | +
- args <- c(list(file = file), dot_args)+ # During the app loading, assign is called only once. |
|||
262 | -3x | +655 | +57x |
- x_fun$set_args(args)+ private$pull_callable$assign_to_env( |
263 | -+ | |||
656 | +57x |
-
+ x = var_name, |
||
264 | -3x | +657 | +57x |
- x <- dataset_connector(+ value = if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) { |
265 | -3x | +658 | +48x |
- dataname = dataname,+ get_raw_data(var_value) |
266 | -3x | +|||
659 | +
- pull_callable = x_fun,+ } else { |
|||
267 | -3x | +|||
660 | +! |
- keys = keys,+ var_value |
||
268 | -3x | +|||
661 | +
- label = label,+ } |
|||
269 | -3x | +|||
662 | +
- code = code_from_script(code, script),+ ) |
|||
270 | -3x | +|||
663 | +
- metadata = metadata+ } |
|||
271 | +664 |
- )+ } |
||
272 | +665 |
-
+ # eval CallableFunction with dynamic args |
||
273 | -3x | +666 | +118x |
- return(x)+ tryCatch( |
274 | -+ | |||
667 | +118x |
- }+ expr = private$pull_callable$run(args = args, try = try), |
||
275 | -+ | |||
668 | +118x |
-
+ error = function(e) { |
||
276 | -+ | |||
669 | +2x |
- #' `RDS` `CDSICTealDatasetConnector`+ if (grepl("object 'conn' not found", e$message)) { |
||
277 | -+ | |||
670 | +! |
- #'+ output_message <- "This dataset connector requires connection object (conn) to be provided." |
||
278 | +671 |
- #' `r lifecycle::badge("stable")`+ } else { |
||
279 | -+ | |||
672 | +2x |
- #'+ output_message <- paste("Could not pull dataset, the following error message was returned:", e$message) |
||
280 | +673 |
- #' Create a `CDSICTealDatasetConnector` from `RDS` file with keys automatically+ } |
||
281 | -+ | |||
674 | +2x |
- #' assigned by `dataname`+ stop(output_message, call. = FALSE) |
||
282 | +675 |
- #'+ } |
||
283 | +676 |
- #' @inheritParams rds_dataset_connector+ ) |
||
284 | +677 |
- #' @inheritParams cdisc_dataset_connector+ }, |
||
285 | +678 |
- #'+ set_failure = function(res) { |
||
286 | -+ | |||
679 | +! |
- #' @rdname rds_dataset_connector+ if (inherits(res, "error")) { |
||
287 | -+ | |||
680 | +! |
- #'+ private$failed <- TRUE |
||
288 | -+ | |||
681 | +! |
- #' @export+ private$failure_msg <- conditionMessage(res) |
||
289 | +682 |
- rds_cdisc_dataset_connector <- function(dataname,+ } else { |
||
290 | -+ | |||
683 | +! |
- file,+ private$failed <- FALSE+ |
+ ||
684 | +! | +
+ private$failure_msg <- NULL |
||
291 | +685 |
- keys = get_cdisc_keys(dataname),+ }+ |
+ ||
686 | +! | +
+ return(NULL) |
||
292 | +687 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ }, |
||
293 | +688 |
- label = character(0),+ set_var_r6 = function(vars) { |
||
294 | -+ | |||
689 | +227x |
- code = character(0),+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
||
295 | -+ | |||
690 | +227x |
- script = character(0),+ for (varname in names(vars)) { |
||
296 | -+ | |||
691 | +91x |
- metadata = list(type = "rds", file = file),+ var <- vars[[varname]] |
||
297 | +692 |
- ...) {+ |
||
298 | -2x | +693 | +91x |
- x <- rds_dataset_connector(+ if (inherits(var, "TealDatasetConnector") || inherits(var, "TealDataset")) { |
299 | -2x | +694 | +80x |
- dataname = dataname,+ var_deps <- var$get_var_r6() |
300 | -2x | +695 | +80x |
- file = file,+ var_deps[[varname]] <- var |
301 | -2x | +696 | +80x |
- keys = keys,+ for (var_dep_name in names(var_deps)) { |
302 | -2x | +697 | +85x |
- code = code_from_script(code, script),+ var_dep <- var_deps[[var_dep_name]] |
303 | -2x | +698 | +85x |
- label = label,+ if (identical(self, var_dep)) { |
304 | -2x | +|||
699 | +! |
- metadata = metadata,+ stop("Circular dependencies detected") |
||
305 | +700 |
- ...+ } |
||
306 | -+ | |||
701 | +85x |
- )+ private$var_r6[[var_dep_name]] <- var_dep |
||
307 | +702 |
-
+ } |
||
308 | -1x | +|||
703 | +
- res <- as_cdisc(+ } |
|||
309 | -1x | +|||
704 | +
- x,+ } |
|||
310 | -1x | +705 | +227x |
- parent = parent+ return(invisible(self)) |
311 | +706 |
- )+ }, |
||
312 | +707 |
-
+ set_dataname = function(dataname) { |
||
313 | -1x | +708 | +182x |
- return(res)+ checkmate::assert_string(dataname) |
314 | -+ | |||
709 | +182x |
- }+ stopifnot(!grepl("\\s", dataname)) |
||
315 | -+ | |||
710 | +182x |
-
+ private$dataname <- dataname |
||
316 | -+ | |||
711 | +182x |
-
+ return(invisible(self)) |
||
317 | +712 |
- # SCRIPT ====+ }, |
||
318 | +713 |
- #' Script `TealDatasetConnector`+ set_ui = function(ui_args = NULL) { |
||
319 | -+ | |||
714 | +! |
- #'+ private$ui <- function(id) { |
||
320 | -+ | |||
715 | +! |
- #' `r lifecycle::badge("stable")`+ ns <- NS(id) |
||
321 | +716 |
- #'+ # add namespace to input ids |
||
322 | -+ | |||
717 | +! |
- #' Create a `TealDatasetConnector` from `.R` file.+ ui <- if (!is.null(ui_args)) { |
||
323 | -+ | |||
718 | +! |
- #'+ do.call(ui_args, list(ns = ns)) |
||
324 | +719 |
- #' @inheritParams dataset_connector+ } else { |
||
325 | -+ | |||
720 | +! |
- #' @inheritParams fun_dataset_connector+ NULL |
||
326 | +721 |
- #' @param file (`character`)\cr+ } |
||
327 | +722 |
- #' file location containing code to be evaluated in connector. Object obtained in the last+ # check ui inputs |
||
328 | -+ | |||
723 | +! |
- #' call from file will be returned to the connector - same as `source(file = file)$value`+ if (!is.null(ui)) { |
||
329 | -+ | |||
724 | +! |
- #'+ checkmate::assert_list(ui, types = "shiny.tag") |
||
330 | -+ | |||
725 | +! |
- #' @export+ attr_class <- vapply(lapply(ui, "[[", i = "attribs"), "[[", character(1), i = "class") |
||
331 | -+ | |||
726 | +! |
- #'+ if (!all(grepl("shiny-input-container", attr_class))) { |
||
332 | -+ | |||
727 | +! |
- #' @rdname script_dataset_connector+ stop("All elements must be shiny inputs") |
||
333 | +728 |
- #'+ } |
||
334 | +729 |
- #' @examples+ } |
||
335 | +730 |
- #' \dontrun{+ # create ui |
||
336 | -+ | |||
731 | +! |
- #' x <- script_dataset_connector(+ if (!is.null(ui)) { |
||
337 | -+ | |||
732 | +! |
- #' dataname = "ADSL",+ tags$div( |
||
338 | -+ | |||
733 | +! |
- #' file = "path/to/script.R",+ tags$div( |
||
339 | -+ | |||
734 | +! |
- #' keys = get_cdisc_keys("ADSL")+ id = ns("inputs"), |
||
340 | -+ | |||
735 | +! |
- #' )+ h4("TealDataset Connector for ", code(self$get_dataname())), |
||
341 | -+ | |||
736 | +! |
- #' x$get_code()+ ui |
||
342 | +737 |
- #' }+ ) |
||
343 | +738 |
- script_dataset_connector <- function(dataname,+ ) |
||
344 | +739 |
- file,+ } |
||
345 | +740 |
- keys = character(0),+ } |
||
346 | -+ | |||
741 | +! |
- label = character(0),+ return(invisible(self)) |
||
347 | +742 |
- code = character(0),+ } |
||
348 | +743 |
- script = character(0),+ ) |
||
349 | +744 |
- metadata = NULL,+ ) |
350 | +1 |
- ...) {- |
- ||
351 | -4x | -
- vars <- list(...)- |
- ||
352 | -4x | -
- checkmate::assert_list(vars, min.len = 0, names = "unique")- |
- ||
353 | -4x | -
- checkmate::assert_string(file)- |
- ||
354 | -4x | -
- if (!file.exists(file)) {- |
- ||
355 | -1x | -
- stop("File ", file, " does not exist.", call. = FALSE)+ #' Get code |
||
356 | +2 |
- }+ #' |
||
357 | +3 |
-
+ #' @description `r lifecycle::badge("stable")` |
||
358 | -3x | +|||
4 | +
- x_fun <- callable_function(source) # nolint+ #' Reads code from specified files or an R6 object. |
|||
359 | -3x | +|||
5 | +
- x_fun$set_args(list(file = file, local = TRUE))+ #' |
|||
360 | +6 |
-
+ #' \itemize{ |
||
361 | -3x | +|||
7 | +
- x <- dataset_connector(+ #' \item{if reading from R6: }{get the R code stored inside the object.} |
|||
362 | -3x | +|||
8 | +
- dataname = dataname,+ #' \item{if reading from files: }{ |
|||
363 | -3x | +|||
9 | +
- pull_callable = x_fun,+ #' Includes code from source if reading from files. Method reads code without |
|||
364 | -3x | +|||
10 | +
- keys = keys,+ #' } |
|||
365 | -3x | +|||
11 | +
- label = label,+ #' } |
|||
366 | -3x | +|||
12 | +
- code = code_from_script(code, script),+ #' `library()` or `require()` calls. Function created for teal app, but can be used with any file. |
|||
367 | -3x | +|||
13 | +
- vars = vars,+ #' Get code from certain files and for specific datasets |
|||
368 | -3x | +|||
14 | +
- metadata = metadata+ #' |
|||
369 | +15 |
- )+ #' Reads code from specified files and specific code chunks. |
||
370 | +16 | - - | -||
371 | -3x | +|||
17 | +
- return(x)+ #' Code chunks are described with: |
|||
372 | +18 |
- }+ #' |
||
373 | +19 |
-
+ #' \itemize{ |
||
374 | +20 |
- #' Script `CDISCTealDatasetConnector`+ #' \item{to open chunk }{`#code>` or `#code ADSL>` or `#code ADSL ADTTE>`} |
||
375 | +21 |
- #'+ #' \item{to close chunk }{`#<code` or `#<ADSL code` or `#<ADSL ADTTE code`} |
||
376 | +22 |
- #' `r lifecycle::badge("stable")`+ #' } |
||
377 | +23 |
#' |
||
378 | +24 |
- #' Create a `CDISCTealDatasetConnector` from `script` file with keys assigned+ #' @param x ([`TealDatasetConnector`] or [`TealDataset`]). If of class `character` will be treated as file to read. |
||
379 | +25 |
- #' automatically by `dataname`.+ #' @param exclude_comments (`logical`) whether exclude commented-out lines of code. Lines to be excluded |
||
380 | +26 |
- #'+ #' should be ended with `# nocode`. For multiple line exclusions one should enclose ignored block of code with |
||
381 | +27 |
- #' @inheritParams script_dataset_connector+ #' `# nocode>` and `# <nocode` |
||
382 | +28 |
- #' @inheritParams cdisc_dataset_connector+ #' @param read_sources (`logical`) whether to replace `source("path")` with code lines from sourced file. |
||
383 | +29 |
- #'+ #' If `read_sources = TRUE` changing working directory inside preprocessing is not allowed. |
||
384 | +30 |
- #' @rdname script_dataset_connector+ #' @param deparse (`logical`) whether return deparsed form of a call |
||
385 | +31 |
- #'+ #' @param files_path (`character`) (optional) vector of files path to be read for preprocessing. Code from |
||
386 | +32 |
- #' @export+ #' multiple files is joined together. |
||
387 | +33 |
- script_cdisc_dataset_connector <- function(dataname,+ #' @param dataname (`character`) Name of dataset to return code for. |
||
388 | +34 |
- file,+ #' @param ... not used, only for support of S3 |
||
389 | +35 |
- keys = get_cdisc_keys(dataname),+ #' @export |
||
390 | +36 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ #' @return (`character`) code of import and preparation of data for teal application. |
||
391 | +37 |
- label = character(0),+ get_code <- function(x, ...) {+ |
+ ||
38 | +59x | +
+ UseMethod("get_code") |
||
392 | +39 |
- code = character(0),+ } |
||
393 | +40 |
- script = character(0),+ |
||
394 | +41 |
- metadata = NULL,+ |
||
395 | +42 |
- ...) {+ # Getting code from R6 ==== |
||
396 | -1x | +|||
43 | +
- x <- script_dataset_connector(+ |
|||
397 | -1x | +|||
44 | +
- dataname = dataname,+ #' @export |
|||
398 | -1x | +|||
45 | +
- file = file,+ #' @rdname get_code |
|||
399 | -1x | +|||
46 | +
- keys = keys,+ get_code.TealDatasetConnector <- function(x, deparse = TRUE, ...) { |
|||
400 | -1x | +47 | +5x |
- code = code_from_script(code, script),+ check_ellipsis(...) |
401 | -1x | +48 | +5x |
- script = script,+ x$get_code(deparse = deparse) |
402 | -1x | +|||
49 | +
- label = label,+ } |
|||
403 | -1x | +|||
50 | +
- metadata = metadata,+ |
|||
404 | +51 |
- ...+ #' @export |
||
405 | +52 |
- )+ #' @rdname get_code |
||
406 | +53 |
-
+ get_code.TealDataset <- function(x, deparse = TRUE, ...) { |
||
407 | -1x | +54 | +12x |
- res <- as_cdisc(+ check_ellipsis(...) |
408 | -1x | +55 | +12x |
- x,+ x$get_code(deparse = deparse) |
409 | -1x | +|||
56 | +
- parent = parent+ } |
|||
410 | +57 |
- )+ |
||
411 | +58 | |||
412 | -1x | +|||
59 | +
- return(res)+ #' @rdname get_code |
|||
413 | +60 |
- }+ #' @export |
||
414 | +61 |
-
+ #' @examples |
||
415 | +62 |
-
+ #' x1 <- dataset( |
||
416 | +63 |
- # CODE ====+ #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE), |
||
417 | +64 |
- #' Code `TealDatasetConnector`+ #' keys = "y", |
||
418 | +65 |
- #'+ #' dataname = "XY", |
||
419 | +66 |
- #' `r lifecycle::badge("stable")`+ #' code = "XY <- data.frame(x = c(1, 2), y = c('aa', 'bb'), stringsAsFactors = FALSE)", |
||
420 | +67 |
- #'+ #' label = character(0) |
||
421 | +68 |
- #' Create a `TealDatasetConnector` from a string of code.+ #' ) |
||
422 | +69 |
#' |
||
423 | +70 |
- #' @inheritParams dataset_connector+ #' x2 <- dataset( |
||
424 | +71 |
- #' @inheritParams fun_dataset_connector+ #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE), |
||
425 | +72 |
- #'+ #' keys = "y", |
||
426 | +73 |
- #' @param code (`character`)\cr+ #' dataname = "XYZ", |
||
427 | +74 |
- #' String containing the code to produce the object.+ #' code = "XYZ <- data.frame(x = c(1, 2), y = c('aa', 'bb'), stringsAsFactors = FALSE)", |
||
428 | +75 |
- #' The code must end in a call to the object.+ #' label = character(0) |
||
429 | +76 |
- #' @param mutate_code (`character`)\cr+ #' ) |
||
430 | +77 |
- #' String containing the code used to mutate the object+ #' |
||
431 | +78 |
- #' after it is produced.+ #' rd <- teal_data(x1, x2) |
||
432 | +79 |
- #' @param mutate_script (`character`)\cr+ #' |
||
433 | +80 |
- #' Alternatively to `mutate_code` - location of the file containing modification code.+ #' get_code(rd) |
||
434 | +81 |
- #' Can't be used simultaneously with `mutate_script`.+ #' get_code(rd, "XY") |
||
435 | +82 |
- #'+ #' get_code(rd, "XYZ") |
||
436 | +83 |
- #' @export+ get_code.TealDataAbstract <- function(x, dataname = character(0), deparse = TRUE, ...) { # nolint |
||
437 | -+ | |||
84 | +7x |
- #'+ check_ellipsis(...) |
||
438 | -+ | |||
85 | +7x |
- #' @rdname code_dataset_connector+ if (length(dataname) > 0) { |
||
439 | -+ | |||
86 | +4x |
- #'+ if (any(!(dataname %in% x$get_datanames()))) { |
||
440 | -+ | |||
87 | +! |
- #' @examples+ stop("The dataname provided does not exist") |
||
441 | +88 |
- #' x <- code_dataset_connector(+ } |
||
442 | -+ | |||
89 | +4x |
- #' dataname = "ADSL",+ x$get_code(dataname = dataname, deparse = deparse) |
||
443 | +90 |
- #' keys = get_cdisc_keys("ADSL"),+ } else { |
||
444 | -+ | |||
91 | +3x |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\"); ADSL"+ x$get_code(deparse = deparse) |
||
445 | +92 |
- #' )+ } |
||
446 | +93 |
- #'+ } |
||
447 | +94 |
- #' x$get_code()+ |
||
448 | +95 |
- #'+ # Getting code from files ==== |
||
449 | +96 |
- #' mutate_dataset(x, code = "ADSL$new_variable <- 1")+ |
||
450 | +97 |
- #' x$get_code()+ #' @rdname get_code |
||
451 | +98 |
- #'+ #' @export |
||
452 | +99 |
- #' file_example <- tempfile(fileext = ".R")+ get_code.default <- function(x, |
||
453 | +100 |
- #' writeLines(+ exclude_comments = TRUE, |
||
454 | +101 |
- #' text = c(+ read_sources = TRUE, |
||
455 | +102 |
- #' "seed <- 1; ADSL <- radsl(cached = TRUE, seed = seed)\nADSL"+ deparse = FALSE, |
||
456 | +103 |
- #' ),+ files_path = NULL, |
||
457 | +104 |
- #' con = file_example+ dataname = NULL, |
||
458 | +105 |
- #' )+ ...) { |
||
459 | -+ | |||
106 | +35x |
- #'+ if (!is.null(files_path)) { |
||
460 | -+ | |||
107 | +31x |
- #' y <- code_dataset_connector(+ x <- files_path |
||
461 | +108 |
- #' dataname = "ADSL",+ } |
||
462 | +109 |
- #' keys = get_cdisc_keys("ADSL"),+ |
||
463 | -+ | |||
110 | +35x |
- #' code = paste0(readLines(file_example), collapse = "\n")+ check_ellipsis(...) |
||
464 | -+ | |||
111 | +35x |
- #' )+ checkmate::assert_character(x, min.len = 1, any.missing = FALSE) |
||
465 | -+ | |||
112 | +35x |
- code_dataset_connector <- function(dataname,+ checkmate::assert_flag(exclude_comments) |
||
466 | -+ | |||
113 | +32x |
- code,+ checkmate::assert_flag(read_sources) |
||
467 | +114 |
- keys = character(0),+ |
||
468 | -+ | |||
115 | +30x |
- label = character(0),+ if (!methods::hasArg(dataname)) { |
||
469 | -+ | |||
116 | +11x |
- mutate_code = character(0),+ l_lines <- lapply(x, function(file_path) { |
||
470 | -+ | |||
117 | +11x |
- mutate_script = character(0),+ code_exclude( |
||
471 | -+ | |||
118 | +11x | +
+ enclosed_with(+ |
+ ||
119 | +11x |
- metadata = NULL,+ get_code_single(file_path, read_sources = read_sources) |
||
472 | +120 |
- ...) {+ ), |
||
473 | -6x | +121 | +11x |
- vars <- list(...)+ lines, |
474 | -6x | +122 | +11x |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ exclude_comments = exclude_comments |
475 | -6x | +|||
123 | +
- checkmate::assert_string(code)+ ) |
|||
476 | -6x | +|||
124 | +
- checkmate::assert_character(label, max.len = 1, any.missing = FALSE)+ }) |
|||
477 | +125 |
-
+ } else { |
||
478 | -6x | +126 | +19x |
- call <- callable_code(code = code)+ l_lines <- lapply(x, function(file_path) { |
479 | -+ | |||
127 | +19x |
-
+ code_exclude( |
||
480 | -6x | +128 | +19x |
- x <- dataset_connector(+ enclosed_with_dataname( |
481 | -6x | +129 | +19x |
- dataname = dataname,+ get_code_single(file_path, read_sources = read_sources), |
482 | -6x | +130 | +19x |
- pull_callable = call,+ dataname = dataname |
483 | -6x | +|||
131 | +
- keys = keys,+ ), |
|||
484 | -6x | +132 | +19x |
- label = label,+ lines, |
485 | -6x | +133 | +19x |
- code = code_from_script(mutate_code, mutate_script),+ exclude_comments = exclude_comments |
486 | -6x | +|||
134 | +
- vars = vars,+ ) |
|||
487 | -6x | +|||
135 | +
- metadata = metadata+ }) |
|||
488 | +136 |
- )+ } |
||
489 | +137 | |||
490 | -6x | +138 | +27x |
- return(x)+ lines <- unlist(l_lines) |
491 | -+ | |||
139 | +27x |
- }+ if (deparse) { |
||
492 | -+ | |||
140 | +! |
-
+ return(paste( |
||
493 | -+ | |||
141 | +! |
- #' Code `CDISCTealDatasetConnector`+ vapply(lines, FUN = deparse1, collapse = "\n", FUN.VALUE = character(1)), |
||
494 | -+ | |||
142 | +! |
- #'+ collapse = "\n" |
||
495 | +143 |
- #' `r lifecycle::badge("stable")`+ )) |
||
496 | +144 |
- #'+ } else {+ |
+ ||
145 | +27x | +
+ return(paste(lines, collapse = "\n")) |
||
497 | +146 |
- #' Create a `CDISCTealDatasetConnector` from a string of code with keys+ } |
||
498 | +147 |
- #' assigned automatically by `dataname`.+ } |
||
499 | +148 |
- #'+ |
||
500 | +149 |
- #' @inheritParams code_dataset_connector+ |
||
501 | +150 |
- #' @inheritParams cdisc_dataset_connector+ |
||
502 | +151 |
- #'+ # * Sub functions for getting code from files ==== |
||
503 | +152 |
- #' @rdname code_dataset_connector+ |
||
504 | +153 |
- #'+ #' Get code |
||
505 | +154 |
- #' @export+ #' |
||
506 | +155 |
- code_cdisc_dataset_connector <- function(dataname,+ #' Get code from specified file. |
||
507 | +156 |
- code,+ #' @param file_path (`character`) path or URL address of the file to be parsed |
||
508 | +157 |
- keys = get_cdisc_keys(dataname),+ #' @param if_url (`logical`) (optional) TRUE when URL address is provided |
||
509 | +158 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ #' @inheritParams get_code |
||
510 | +159 |
- label = character(0),+ #' |
||
511 | +160 |
- mutate_code = character(0),+ #' @return lines (`character`) of preprocessing code |
||
512 | +161 |
- metadata = NULL,+ #' @keywords internal |
||
513 | +162 |
- ...) {+ get_code_single <- function(file_path, read_sources, if_url = grepl("^http[s]", file_path)) { |
||
514 | -1x | +163 | +84x |
- x <- code_dataset_connector(+ checkmate::assert_string(file_path) |
515 | -1x | +164 | +84x |
- dataname = dataname,+ if (!if_url) { |
516 | -1x | +165 | +84x |
- code = code,+ if (!file.exists(file_path)) { |
517 | -1x | +166 | +2x |
- keys = keys,+ stop( |
518 | -1x | +167 | +2x |
- mutate_code = mutate_code,+ "Reading preprocessing code from ", file_path, " file failed. ", |
519 | -1x | +168 | +2x |
- label = label,+ "Please double check if you saved your script." |
520 | -1x | +|||
169 | +
- metadata = metadata,+ ) |
|||
521 | +170 |
- ...+ } |
||
522 | +171 |
- )+ }+ |
+ ||
172 | +82x | +
+ checkmate::assert_flag(read_sources)+ |
+ ||
173 | +82x | +
+ checkmate::assert_flag(if_url) |
||
523 | +174 | |||
524 | -1x | +175 | +82x |
- res <- as_cdisc(+ lines <- readLines(file_path) |
525 | -1x | +176 | +82x |
- x,+ if (read_sources) { |
526 | -1x | +177 | +80x |
- parent = parent+ lines <- include_source_code(lines = lines, dir = `if`(if_url, NULL, dirname(file_path))) |
527 | +178 |
- )+ } |
||
528 | +179 | |||
529 | -1x | +180 | +82x |
- return(res)+ lines |
530 | +181 |
} |
||
531 | +182 | |||
532 | +183 |
- # CSV ====+ #' Get code enclosed within |
||
533 | +184 |
- #' `csv` `TealDatasetConnector`+ #' |
||
534 | +185 |
- #'+ #' Extracts lines from code which are enclosed within regexp starts_at and stops_at |
||
535 | +186 |
- #' `r lifecycle::badge("stable")`+ #' @param lines (`character`) of preprocessing code. |
||
536 | +187 |
- #'+ #' @return (`character`) subset of lines which start and end with preprocessing |
||
537 | +188 |
- #' Create a `TealDatasetConnector` from `csv` (or general delimited file).+ #' start and stop tags. |
||
538 | +189 |
- #'+ #' @keywords internal |
||
539 | +190 |
- #'+ enclosed_with <- function(lines) { |
||
540 | -+ | |||
191 | +11x |
- #' @inheritParams dataset_connector+ checkmate::assert_character(lines, min.len = 1, any.missing = FALSE) |
||
541 | +192 |
- #' @inheritParams fun_dataset_connector+ |
||
542 | +193 |
- #'+ # set beginning of preprocessing |
||
543 | -+ | |||
194 | +9x |
- #' @param file (`character`)\cr+ idx_start <- grep("#\\s*code>", lines) |
||
544 | -+ | |||
195 | +9x |
- #' path to (`.csv)` (or general delimited) file that contains `data.frame` object+ line_starts <- if (length(idx_start) > 1) { |
||
545 | -+ | |||
196 | +! |
- #'+ warning("More than one preproc start found - using the first one.") |
||
546 | -+ | |||
197 | +! |
- #' @param ... (`optional`)\cr+ idx_start[1] + 1 |
||
547 | -+ | |||
198 | +9x |
- #' additional arguments applied to pull function (`readr::read_delim`) by default+ } else if (length(idx_start) == 1) { |
||
548 | -+ | |||
199 | +7x |
- #' `delim = ","`.+ idx_start + 1 |
||
549 | +200 |
- #'+ } else {+ |
+ ||
201 | +2x | +
+ 1L |
||
550 | +202 |
- #' @export+ } |
||
551 | +203 |
- #'+ |
||
552 | +204 |
- #' @rdname csv_dataset_connector+ # set stop of preprocessing+ |
+ ||
205 | +9x | +
+ idx_stop <- grep("#\\s*<code", lines)+ |
+ ||
206 | +9x | +
+ line_stops <- if (length(idx_stop) > 1) {+ |
+ ||
207 | +! | +
+ warning("More than one preproc stops found - using the last one.")+ |
+ ||
208 | +! | +
+ utils::tail(idx_stop, 1) - 1+ |
+ ||
209 | +9x | +
+ } else if (length(idx_stop) == 1) {+ |
+ ||
210 | +7x | +
+ idx_stop - 1 |
||
553 | +211 |
- #'+ } else {+ |
+ ||
212 | +2x | +
+ length(lines) |
||
554 | +213 |
- #' @examples+ } |
||
555 | +214 |
- #' \dontrun{+ |
||
556 | -+ | |||
215 | +9x |
- #' x <- csv_dataset_connector(+ line_numbers <- seq(line_starts, line_stops) |
||
557 | +216 |
- #' dataname = "ADSL",+ |
||
558 | -+ | |||
217 | +9x |
- #' file = "path/to/file.csv",+ lines[line_numbers] |
||
559 | +218 |
- #' delim = ",",+ } |
||
560 | +219 |
- #' col_types = quote(readr::cols(AGE = "i"))+ |
||
561 | +220 |
- #' )+ #' Get code enclosed within |
||
562 | +221 |
- #' x$get_code()+ #' |
||
563 | +222 |
- #' }+ #' Extracts lines from code which are enclosed within regexp starts_at and stops_at |
||
564 | +223 |
- csv_dataset_connector <- function(dataname,+ #' @inheritParams enclosed_with |
||
565 | +224 |
- file,+ #' @param dataname (`character`) metadata for returned lines |
||
566 | +225 |
- keys = character(0),+ #' @return (`list`) list of lines and their numbers from certain chunks of code at the specific file. |
||
567 | +226 |
- label = character(0),+ #' @keywords internal |
||
568 | +227 |
- code = character(0),+ enclosed_with_dataname <- function(lines, dataname = NULL) { |
||
569 | -+ | |||
228 | +21x |
- script = character(0),+ checkmate::assert_character(lines, min.len = 1, any.missing = FALSE) |
||
570 | -+ | |||
229 | +21x |
- metadata = list(type = "csv", file = file),+ if (!checkmate::test_character(dataname, min.len = 1, any.missing = FALSE)) {+ |
+ ||
230 | +4x | +
+ dataname <- "" |
||
571 | +231 |
- ...) {+ } |
||
572 | -13x | +232 | +21x |
- dot_args <- list(...)+ dataname <- trimws(dataname) |
573 | -13x | +233 | +21x |
- checkmate::assert_list(dot_args, min.len = 0, names = "unique")+ any_chunk <- any(grepl("#\\s*<?\\s*code", lines)) |
574 | +234 | |||
575 | -13x | +235 | +21x |
- check_pkg_quietly(+ if (any_chunk) { |
576 | -13x | +236 | +17x |
- "readr",+ any_start <- any(grepl(sprintf("#\\s*code[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*>", dataname), lines, perl = TRUE)) |
577 | -13x | +237 | +17x |
- "library readr is required to use csv connectors please install it."+ any_stop <- any(grepl(sprintf("#\\s*<[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*(?<![a-zA-Z])code", dataname), lines, perl = TRUE)) |
578 | +238 |
- )+ |
||
579 | -+ | |||
239 | +17x |
-
+ if (!(any_start && any_stop)) { |
||
580 | -+ | |||
240 | +1x |
- # add default delim as ","+ stop(sprintf("File doesn't contain code marked for this %1$s.\n |
||
581 | -13x | +241 | +1x |
- if (!"delim" %in% names(dot_args)) {+ Please use # code %1$s> to indicate which lines should be extracted.", dataname)) |
582 | -6x | +|||
242 | +
- dot_args$delim <- ","+ } |
|||
583 | +243 |
} |
||
584 | +244 | |||
585 | -13x | +|||
245 | +
- checkmate::assert_string(file)+ # set beginning of preprocessing |
|||
586 | -10x | +246 | +20x |
- if (!file.exists(file)) {+ idx_start <- grep(sprintf("#\\s*code(?:[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*|[\\s]*)>", dataname), lines, perl = TRUE) |
587 | -1x | +247 | +20x |
- stop("File ", file, " does not exist.", call. = FALSE)+ line_starts <- if (length(idx_start) >= 1) { |
588 | -+ | |||
248 | +16x |
- }+ idx_start + 1 |
||
589 | +249 |
-
+ } else { |
||
590 | -9x | +250 | +4x |
- x_fun <- callable_function("readr::read_delim") # using read_delim as preserves dates (read.csv does not)+ 1L |
591 | -9x | +|||
251 | +
- args <- c(list(file = file), dot_args)+ } |
|||
592 | -9x | +|||
252 | +
- x_fun$set_args(args)+ |
|||
593 | +253 |
-
+ # set stop of preprocessing |
||
594 | -9x | +254 | +20x |
- x <- dataset_connector(+ idx_stop <- grep( |
595 | -9x | +255 | +20x |
- dataname = dataname,+ sprintf("#\\s*<(?:[\\sa-zA-Z_]*%s[\\sa-zA-Z_]*|[\\s]*)(?<![a-zA-Z])code", dataname), |
596 | -9x | +256 | +20x |
- pull_callable = x_fun,+ lines, |
597 | -9x | +257 | +20x |
- keys = keys,+ perl = TRUE+ |
+
258 | ++ |
+ ) |
||
598 | -9x | +259 | +20x |
- label = label,+ line_stops <- if (length(idx_stop) >= 1) { |
599 | -9x | +260 | +16x |
- code = code_from_script(code, script),+ idx_stop - 1+ |
+
261 | ++ |
+ } else { |
||
600 | -9x | +262 | +4x |
- metadata = metadata+ length(lines) |
601 | +263 |
- )+ } |
||
602 | +264 | |||
603 | -9x | +265 | +20x |
- return(x)+ if (length(line_starts) != length(line_stops) || any(line_starts > line_stops)) {+ |
+
266 | +! | +
+ stop("Number of #code> has to be the same as #<code") |
||
604 | +267 |
- }+ } |
||
605 | +268 | |||
606 | +269 |
- #' `csv` `CDISCTealDatasetConnector`+ |
||
607 | -+ | |||
270 | +20x |
- #'+ ll <- data.frame(line_starts, line_stops) |
||
608 | +271 |
- #' `r lifecycle::badge("stable")`+ |
||
609 | -+ | |||
272 | +20x |
- #'+ line_numbers <- apply(ll, 1, function(x) seq(x[1], x[2])) |
||
610 | +273 |
- #' Create a `CDISCTealDatasetConnector` from `csv` (or general delimited) file+ |
||
611 | -+ | |||
274 | +20x |
- #' with keys and parent name assigned automatically by `dataname`.+ lines_taken <- as.integer(unlist(line_numbers)) |
||
612 | +275 |
- #'+ + |
+ ||
276 | +20x | +
+ res_lines <- lines[lines_taken] |
||
613 | +277 |
- #' @inheritParams csv_dataset_connector+ + |
+ ||
278 | +20x | +
+ return(res_lines) |
||
614 | +279 |
- #' @inheritParams cdisc_dataset_connector+ } |
||
615 | +280 |
- #'+ |
||
616 | +281 |
- #' @rdname csv_dataset_connector+ #' Exclude from code |
||
617 | +282 |
#' |
||
618 | +283 |
- #' @export+ #' Excludes lines from code. It is possible to exclude one line ended by `# nocode` |
||
619 | +284 |
- csv_cdisc_dataset_connector <- function(dataname,+ #' @inheritParams enclosed_with |
||
620 | +285 |
- file,+ #' @inheritParams get_code |
||
621 | +286 |
- keys = get_cdisc_keys(dataname),+ #' @inheritParams get_code_single |
||
622 | +287 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ #' @keywords internal |
||
623 | +288 |
- label = character(0),+ code_exclude <- function(lines, exclude_comments, file_path) {+ |
+ ||
289 | +32x | +
+ checkmate::assert_character(lines, min.len = 1, any.missing = FALSE)+ |
+ ||
290 | +29x | +
+ checkmate::assert_flag(exclude_comments) |
||
624 | +291 |
- code = character(0),+ + |
+ ||
292 | +29x | +
+ nocode_single <- grep("^.+#[[:space:]]*nocode", lines)+ |
+ ||
293 | +29x | +
+ nocode_start <- grep("[[:space:]]*#[[:space:]]*nocode[[:space:]]*>+", lines)+ |
+ ||
294 | +29x | +
+ nocode_stop <- grep("[[:space:]]*#[[:space:]]*<+[[:space:]]*nocode[[:space:]]*", lines) |
||
625 | +295 |
- script = character(0),+ + |
+ ||
296 | +29x | +
+ if (length(nocode_start) != length(nocode_stop)) {+ |
+ ||
297 | +! | +
+ stop(paste("Unequal number of no-code starts and stops in ", file_path)) # nolint |
||
626 | +298 |
- metadata = list(type = "csv", file = file),+ } |
||
627 | +299 |
- ...) {+ |
||
628 | -9x | +300 | +29x |
- x <- csv_dataset_connector(+ nocode_multi <- NULL |
629 | -9x | +301 | +29x |
- dataname = dataname,+ if (length(nocode_start) > 0) { |
630 | -9x | +302 | +10x |
- file = file,+ nocode_multi <- unlist(Map(seq, from = nocode_start, to = nocode_stop)) |
631 | -9x | +|||
303 | +
- keys = keys,+ } |
|||
632 | -9x | +|||
304 | +
- code = code_from_script(code, script),+ |
|||
633 | -9x | +305 | +29x |
- label = label,+ nocode <- c(nocode_single, nocode_multi)+ |
+
306 | ++ | + | ||
634 | -9x | +307 | +29x |
- metadata = metadata,+ if (length(nocode) > 0) { |
635 | -+ | |||
308 | +19x |
- ...+ lines <- lines[-nocode] |
||
636 | +309 |
- )+ } |
||
637 | +310 | |||
638 | -9x | +311 | +29x |
- res <- as_cdisc(+ if (exclude_comments) { |
639 | -9x | +312 | +10x |
- x,+ lines <- grep("^\\s*#.+$", x = lines, invert = TRUE, value = TRUE) |
640 | -9x | +313 | +10x |
- parent = parent+ lines <- gsub("(^\\s*#.+$)|(#[^\'\"]*$)", "", x = lines, perl = TRUE) |
641 | +314 |
- )+ } |
||
642 | +315 | |||
643 | -9x | +316 | +29x |
- return(res)+ lines |
644 | +317 |
} |
||
645 | +318 | |||
646 | -- |
- # FUN ====- |
- ||
647 | +319 |
- #' Function Dataset Connector+ #' Finds lines of code with source call |
||
648 | +320 |
#' |
||
649 | -- |
- #' `r lifecycle::badge("stable")`- |
- ||
650 | +321 |
- #'+ #' Finds lines in preprocessing code where `source()` call is located |
||
651 | +322 |
- #' Create a `TealDatasetConnector` from `function` and its arguments.+ #' @inheritParams enclosed_with |
||
652 | +323 |
- #'+ #' @keywords internal |
||
653 | +324 |
- #' @inheritParams dataset_connector+ find_source_code <- function(lines) { |
||
654 | -+ | |||
325 | +80x |
- #'+ checkmate::assert_character(lines, min.len = 1, any.missing = FALSE) |
||
655 | -+ | |||
326 | +80x |
- #' @param fun (`function`)\cr+ idx <- grep("^[^#]*source\\([\'\"]([A-Za-z0-9_/.]).*\\.R[\'\"].*\\).*$", lines) |
||
656 | +327 |
- #' a custom function to obtain dataset.+ |
||
657 | -+ | |||
328 | +80x |
- #' @param fun_args (`list`)\cr+ if (length(idx) == 0) { |
||
658 | -+ | |||
329 | +45x |
- #' additional arguments for (`func`).+ return(idx) |
||
659 | +330 |
- #' @param func_name (`name`)\cr+ } |
||
660 | +331 |
- #' for internal purposes, please keep it default+ |
||
661 | -+ | |||
332 | +35x |
- #' @param ... Additional arguments applied to pull function.+ if (any(grepl("source\\([^)]*chdir\\s*=\\s*T(RUE)*", x = lines[idx]))) { |
||
662 | -+ | |||
333 | +! |
- #' In case when this object code depends on the `raw_data` from the other+ stop("Preprocessing doesn't handle source(chdir = TRUE)") |
||
663 | +334 |
- #' `TealDataset`, `TealDatasetConnector` object(s) or other constant value,+ } |
||
664 | +335 |
- #' this/these object(s) should be included. Please note that `vars`+ |
||
665 | -+ | |||
336 | +35x |
- #' are included to this object as local `vars` and they cannot be modified+ if (any(grepl("source\\(.+;\\s*source\\(", x = lines[idx]))) { |
||
666 | -+ | |||
337 | +! |
- #' within another dataset.+ stop("Preprocessing doesn't handle multiple sources in one line\n") |
||
667 | +338 |
- #' @export+ } |
||
668 | +339 |
- #'+ |
||
669 | -+ | |||
340 | +35x |
- #' @rdname fun_dataset_connector+ idx |
||
670 | +341 |
- #'+ } |
||
671 | +342 |
- #' @examples+ |
||
672 | +343 |
- #' my_data <- function(...) {+ #' Includes source in preprocessing code lines |
||
673 | +344 |
- #' data.frame(+ #' |
||
674 | +345 |
- #' ID = paste0("ABC_", seq_len(10)),+ #' @inheritParams enclosed_with |
||
675 | +346 |
- #' var1 = rnorm(n = 10),+ #' @param dir of the file where source is called from. |
||
676 | +347 |
- #' var2 = rnorm(n = 10),+ #' @return lines of code with source text included |
||
677 | +348 |
- #' var3 = rnorm(n = 10)+ #' @keywords internal |
||
678 | +349 |
- #' )+ include_source_code <- function(lines, dir = NULL) { |
||
679 | -+ | |||
350 | +80x |
- #' }+ checkmate::assert_character(lines, min.len = 1, any.missing = FALSE) |
||
680 | -+ | |||
351 | +80x |
- #' y <- fun_dataset_connector(+ stopifnot(is.null(dir) || dir.exists(dir)) |
||
681 | +352 |
- #' dataname = "XYZ",+ |
||
682 | +353 |
- #' fun = my_data+ |
||
683 | -+ | |||
354 | +80x |
- #' )+ idx <- find_source_code(lines) |
||
684 | +355 |
- #'+ |
||
685 | -+ | |||
356 | +80x |
- #' y$get_code()+ if (length(idx) == 0) { |
||
686 | -+ | |||
357 | +45x |
- #'+ return(lines) |
||
687 | +358 |
- #' y$pull()+ } |
||
688 | +359 |
- #'+ |
||
689 | -+ | |||
360 | +35x |
- #' get_raw_data(y)+ sources_path <- unname(vapply( |
||
690 | -+ | |||
361 | +35x |
- fun_dataset_connector <- function(dataname,+ lines[idx], |
||
691 | -+ | |||
362 | +35x |
- fun,+ function(x) { |
||
692 | -+ | |||
363 | +52x |
- fun_args = NULL,+ res <- gsub("source\\(.*[\"\']([A-Za-z0-9_/.])", "\\1", strsplit(x, ",")[[1]][1]) |
||
693 | -+ | |||
364 | +52x |
- keys = character(0),+ res <- gsub("[\'\"]", "", res) |
||
694 | -+ | |||
365 | +52x |
- label = character(0),+ res <- gsub(")", "", res) |
||
695 | -+ | |||
366 | +52x |
- code = character(0),+ res |
||
696 | +367 |
- script = character(0),+ }, |
||
697 | -+ | |||
368 | +35x |
- func_name = substitute(fun),+ character(1) |
||
698 | +369 |
- metadata = NULL,+ )) |
||
699 | +370 |
- ...) {+ |
||
700 | -7x | +371 | +35x |
- vars <- list(...)+ if (length(sources_path) != length(idx)) { |
701 | -7x | +|||
372 | +! |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ stop("Couldn't detect R file name from source() call.") |
||
702 | +373 | - - | -||
703 | -7x | -
- stopifnot(is.function(fun))+ } |
||
704 | +374 | |||
705 | -7x | -
- stopifnot(is.list(fun_args) || is.null(fun_args))- |
- ||
706 | -+ | 375 | +35x |
-
+ sources_code <- lapply(sources_path, function(s) { |
707 | -7x | +376 | +52x |
- cal <- if (!is.symbol(func_name)) as.call(func_name) else NULL+ if (grepl("^http[s]", s)) { |
708 | +377 |
-
+ # url detected - do nothing |
||
709 | -7x | +|||
378 | +
- is_pak <- FALSE+ } else { |
|||
710 | -7x | +379 | +52x |
- is_locked <- TRUE+ s <- ifelse(grepl("^(/)|^([\\])|^([A-Za-z]:)", s), s, file.path(dir, s)) |
711 | -7x | +380 | +52x |
- if ((!is.null(cal)) && identical(cal[[1]], as.symbol("::"))) {+ if (!all(file.exists(s))) { |
712 | -5x | +|||
381 | +! |
- pak <- cal[[2]]+ msg <- paste0( |
||
713 | -5x | +|||
382 | +! |
- pak_char <- as.character(pak) # nolint+ "File(s) provided in the source() calls don't exist: \n", |
||
714 | -5x | +|||
383 | +! |
- library(pak_char, character.only = TRUE)+ paste(s[!file.exists(s)], collapse = "\n") |
||
715 | -5x | +|||
384 | +
- func_name <- cal[[3]]+ ) |
|||
716 | -5x | +|||
385 | +! |
- is_pak <- TRUE+ stop(msg) |
||
717 | -5x | +|||
386 | +
- is_locked <- TRUE+ } |
|||
718 | +387 |
- } else {+ |
||
719 | -2x | +388 | +52x |
- is_locked <- environmentIsLocked(environment(fun))+ s <- normalizePath(s) |
720 | +389 |
- }+ } |
||
721 | +390 | |||
722 | -7x | +391 | +52x |
- func_char <- as.character(func_name)+ get_code_single(file_path = s, read_sources = TRUE) |
723 | -- | - - | -||
724 | -7x | +392 | +
- ee <- new.env(parent = parent.env(globalenv()))+ }) |
|
725 | +393 | |||
726 | -7x | +394 | +35x |
- ee$library <- function(...) {+ lines[idx] <- sources_code |
727 | -! | +|||
395 | +35x |
- mc <- match.call()+ lines <- unlist(lines) |
||
728 | -! | +|||
396 | +
- mc[[1]] <- quote(base::library)+ |
|||
729 | -! | +|||
397 | +35x |
- eval(mc, envir = globalenv())+ lines |
||
730 | -! | +|||
398 | +
- this_env <- parent.frame()+ } |
|||
731 | -! | +
1 | +
- if (!identical(this_env, globalenv())) {+ #' Get dataset from `TealDatasetConnector` |
|||
732 | -! | +|||
2 | +
- parent.env(this_env) <- parent.env(globalenv())+ #' |
|||
733 | +3 |
- }+ #' @description `r lifecycle::badge("stable")` |
||
734 | +4 |
- }+ #' |
||
735 | +5 |
-
+ #' Get dataset from `TealDatasetConnector` |
||
736 | +6 |
-
+ #' @param x (`TealDatasetConnector` or `TealDatasetConnector` or `TealDataAbstract`) |
||
737 | -7x | +|||
7 | +
- if (!is_pak && !is_locked) {+ #' @param dataname (`character`) a name of dataset to be retrieved |
|||
738 | -2x | +|||
8 | +
- eval(bquote(.(func_name) <- get(.(func_char), .(environment(fun)))), envir = ee)+ #' @details See `help(TealDataConnector)` and `help(TealData)` for more complex examples. |
|||
739 | -2x | +|||
9 | +
- eval(bquote(.(func_name) <- rlang::set_env(.(func_name), .(ee))), envir = ee)+ #' @return (`TealDataset`) |
|||
740 | +10 |
- }+ #' @export |
||
741 | +11 |
-
+ get_dataset <- function(x, dataname) { |
||
742 | -7x | +12 | +131x |
- x_fun <- CallableFunction$new(fun, env = ee)+ UseMethod("get_dataset") |
743 | -7x | +|||
13 | +
- x_fun$set_args(fun_args)+ } |
|||
744 | +14 | |||
745 | -7x | +|||
15 | +
- vars[[func_char]] <- ee[[func_char]]+ #' @rdname get_dataset |
|||
746 | +16 |
-
+ #' @export |
||
747 | -7x | +|||
17 | +
- x <- dataset_connector(+ #' @examples |
|||
748 | -7x | +|||
18 | +
- dataname = dataname,+ #' |
|||
749 | -7x | +|||
19 | +
- pull_callable = x_fun,+ #' # TealDatasetConnector -------- |
|||
750 | -7x | +|||
20 | +
- keys = keys,+ #' library(magrittr) |
|||
751 | -7x | +|||
21 | +
- code = code_from_script(code, script),+ #' |
|||
752 | -7x | +|||
22 | +
- label = label,+ #' pull_fun_adae <- callable_function(teal.data::example_cdisc_data) %>% |
|||
753 | -7x | +|||
23 | +
- vars = vars,+ #' set_args(list(dataname = "ADAE")) |
|||
754 | -7x | +|||
24 | +
- metadata = metadata+ #' |
|||
755 | +25 |
- )+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
756 | +26 |
-
+ #' |
||
757 | -7x | +|||
27 | +
- return(x)+ #' dc <- dataset_connector( |
|||
758 | +28 |
- }+ #' dataname = "ADAE", pull_callable = pull_fun_adae, |
||
759 | +29 |
-
+ #' keys = get_cdisc_keys("ADSL") |
||
760 | +30 |
- #' Function `CDISCTealDatasetConnector`+ #' ) |
||
761 | +31 |
#' |
||
762 | +32 |
- #' `r lifecycle::badge("stable")`+ #' \dontrun{ |
||
763 | +33 |
- #'+ #' load_dataset(dc) |
||
764 | +34 |
- #' Create a `CDISCTealDatasetConnector` from `function` and its arguments+ #' get_dataset(dc) |
||
765 | +35 |
- #' with keys and parent name assigned automatically by `dataname`.+ #' } |
||
766 | +36 |
#' |
||
767 | +37 |
- #' @inheritParams fun_dataset_connector+ get_dataset.TealDatasetConnector <- function(x, dataname = NULL) { # nolint |
||
768 | -+ | |||
38 | +47x |
- #' @inheritParams cdisc_dataset_connector+ if (!is.null(dataname)) { |
||
769 | -+ | |||
39 | +! |
- #'+ warning("'dataname' argument ignored - TealDatasetConnector can contain only one dataset.") |
||
770 | +40 |
- #' @rdname fun_dataset_connector+ } |
||
771 | -+ | |||
41 | +47x |
- #'+ return(x$get_dataset()) |
||
772 | +42 |
- #' @export+ } |
||
773 | +43 |
- fun_cdisc_dataset_connector <- function(dataname,+ |
||
774 | +44 |
- fun,+ #' @rdname get_dataset |
||
775 | +45 |
- fun_args = NULL,+ #' @export |
||
776 | +46 |
- keys = get_cdisc_keys(dataname),+ #' @examples |
||
777 | +47 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ #' |
||
778 | +48 |
- label = character(0),+ #' # TealDataset -------- |
||
779 | +49 |
- code = character(0),+ #' ADSL <- example_cdisc_data("ADSL") |
||
780 | +50 |
- script = character(0),+ #' x <- dataset("ADSL", ADSL) |
||
781 | +51 |
- func_name = substitute(fun),+ #' |
||
782 | +52 |
- metadata = NULL,+ #' get_dataset(x) |
||
783 | +53 |
- ...) {- |
- ||
784 | -4x | -
- x <- fun_dataset_connector(- |
- ||
785 | -4x | -
- dataname = dataname,+ get_dataset.TealDataset <- function(x, dataname = NULL) { # nolint |
||
786 | -4x | +54 | +84x |
- fun = fun,+ if (!is.null(dataname)) { |
787 | -4x | +|||
55 | +! |
- fun_args = fun_args,+ warning("'dataname' argument ignored - TealDataset can contain only one dataset.") |
||
788 | -4x | +|||
56 | +
- func_name = func_name,+ } |
|||
789 | -4x | +57 | +84x |
- keys = keys,+ return(x$get_dataset()) |
790 | -4x | +|||
58 | +
- label = label,+ } |
|||
791 | -4x | +|||
59 | +
- code = code,+ |
|||
792 | -4x | +|||
60 | +
- script = script,+ #' @rdname get_dataset |
|||
793 | -4x | +|||
61 | +
- metadata = metadata,+ #' @export |
|||
794 | +62 |
- ...+ #' @examples |
||
795 | +63 |
- )+ #' |
||
796 | +64 |
-
+ #' # TealData (not containing connectors) -------- |
||
797 | -4x | +|||
65 | +
- res <- as_cdisc(+ #' adsl <- cdisc_dataset( |
|||
798 | -4x | +|||
66 | +
- x,+ #' dataname = "ADSL", |
|||
799 | -4x | +|||
67 | +
- parent = parent+ #' x = example_cdisc_data("ADSL"), |
|||
800 | +68 |
- )+ #' code = "library(teal.data)\nADSL <- example_cdisc_data(\"ADSL\")" |
||
801 | +69 |
-
+ #' ) |
||
802 | -4x | +|||
70 | +
- return(res)+ #' |
|||
803 | +71 |
- }+ #' adae <- cdisc_dataset( |
||
804 | +72 |
-
+ #' dataname = "ADAE", |
||
805 | +73 |
-
+ #' x = example_cdisc_data("ADAE"), |
||
806 | +74 |
- # PYTHON ====+ #' code = "library(teal.data)\nADAE <- example_cdisc_data(\"ADAE\")" |
||
807 | +75 |
- #' `Python` `TealDatasetConnector`+ #' ) |
||
808 | +76 |
#' |
||
809 | +77 |
- #' `r lifecycle::badge("experimental")`+ #' rd <- teal.data:::TealData$new(adsl, adae) |
||
810 | +78 |
- #' Create a `TealDatasetConnector` from `.py` file or through python code supplied directly.+ #' get_dataset(rd, "ADSL") |
||
811 | +79 |
- #'+ get_dataset.TealDataAbstract <- function(x, dataname = NULL) { |
||
812 | -+ | |||
80 | +! |
- #' @details+ if (is.null(dataname)) { |
||
813 | -+ | |||
81 | +! | +
+ stop(paste(+ |
+ ||
82 | +! |
- #' Note that in addition to the `reticulate` package, support for python requires an+ "To get single dataset from data class one must specify the name of the dataset.", |
||
814 | -+ | |||
83 | +! |
- #' existing python installation. By default, `reticulate` will attempt to use the+ "To get all datasets please use get_datasets()" |
||
815 | +84 |
- #' location `Sys.which("python")`, however the path to the python installation can be+ )) |
||
816 | +85 |
- #' supplied directly via `reticulate::use_python`.+ } |
||
817 | -+ | |||
86 | +! |
- #'+ return(x$get_dataset(dataname = dataname)) |
||
818 | +87 |
- #' The `teal` API for delayed data requires the python code or script to return a+ } |
819 | +1 |
- #' data.frame object. For this, the `pandas` package is required. This can be installed+ #' Get code from script |
|
820 | +2 |
- #' using `reticulate::py_install("pandas")`.+ #' |
|
821 | +3 |
- #'+ #' Get code from script. Switches between `code` and `script` arguments |
|
822 | +4 |
- #' Please see the package documentation for more details.+ #' to return non-empty one to pass it further to constructors. |
|
823 | +5 |
#' |
|
824 | +6 |
- #' @inheritParams dataset_connector+ #' @param code (`character`)\cr |
|
825 | +7 |
- #' @inheritParams code_dataset_connector+ #' an R code to be evaluated or a `PythonCodeClass` created using [python_code]. |
|
826 | +8 |
- #' @param file (`character`)\cr+ #' @inheritParams dataset_connector |
|
827 | +9 |
- #' Path to the file location containing the python script used to generate the object.+ #' @return code (`character`) |
|
828 | +10 |
- #' @param code (`character`)\cr+ #' @keywords internal |
|
829 | +11 |
- #' string containing the python code to be run using `reticulate`. Carefully consider+ code_from_script <- function(code, script, dataname = NULL) { |
|
830 | -+ | ||
12 | +249x |
- #' indentation to follow proper python syntax.+ checkmate::assert( |
|
831 | -+ | ||
13 | +249x |
- #' @param object (`character`)\cr+ checkmate::check_character(code, max.len = 1, any.missing = FALSE), |
|
832 | -+ | ||
14 | +249x |
- #' name of the object from the python script that is assigned to the dataset to be used.+ checkmate::check_class(code, "PythonCodeClass") |
|
833 | +15 |
- #'+ ) |
|
834 | -+ | ||
16 | +247x |
- #' @note+ checkmate::assert_character(script, max.len = 1, any.missing = FALSE) |
|
835 | -+ | ||
17 | +247x |
- #' Raises an error when passed `code` and `file` are passed at the same time.+ if (length(code) == 0 && length(script) == 0) { |
|
836 | -+ | ||
18 | +182x |
- #'+ return(character(0)) |
|
837 | +19 |
- #' When using `code`, keep in mind that when using `reticulate` with delayed data, python+ } |
|
838 | +20 |
- #' functions do not have access to other objects in the `code` and must be self contained.+ |
|
839 | -+ | ||
21 | +65x |
- #' In the following example, the function `makedata()` doesn't have access to variable `x`:+ if (checkmate::test_string(code) && checkmate::test_string(script)) { |
|
840 | -+ | ||
22 | +! |
- #'+ stop("Function doesn't accept 'code' and 'script' at the same time. |
|
841 | -+ | ||
23 | +! |
- #' \preformatted{import pandas as pd+ Please specify either 'code' or 'script'", call. = FALSE) |
|
842 | +24 |
- #'+ } |
|
843 | +25 |
- #' x = 1+ |
|
844 | -+ | ||
26 | +65x |
- #' def makedata():+ if (checkmate::test_string(script)) { |
|
845 | -+ | ||
27 | +! |
- #' return pd.DataFrame({'x': [x, 2], 'y': [3, 4]})+ code <- read_script(file = script, dataname = dataname) |
|
846 | +28 |
- #'+ } |
|
847 | +29 |
- #' data = makedata()}+ |
|
848 | -+ | ||
30 | +65x |
- #'+ code |
|
849 | +31 |
- #' When using custom functions, the function environment must be entirely self contained:+ } |
|
850 | +32 |
- #'+ |
|
851 | +33 |
- #' \preformatted{def makedata():+ #' Read .R file into character |
|
852 | +34 |
- #' import pandas as pd+ #' |
|
853 | +35 |
- #' x = 1+ #' @description `r lifecycle::badge("stable")` |
|
854 | +36 |
- #' return pd.DataFrame({'x': [x, 2], 'y': [3, 4]})+ #' Comments will be excluded |
|
855 | +37 |
#' |
|
856 | +38 |
- #' data = makedata()+ #' @param file (`character`) File to be parsed into code |
|
857 | +39 |
- #' }+ #' @param dataname (`character`) dataset name to subset code from chunks |
|
858 | +40 |
- #'+ #' @return (`character`) vector with the code |
|
859 | +41 |
- #' **Additional `reticulate` considerations:**+ #' |
|
860 | +42 |
- #' 1. Note that when using pull `vars`, `R` objects referenced in the python+ #' @export |
|
861 | +43 |
- #' code or script have to be prefixed with `r.`.+ #' @examples |
|
862 | +44 |
- #' 2. `reticulate` isn't able to convert `POSIXct` objects. Please take extra+ #' file_example <- tempfile() |
|
863 | +45 |
- #' care when working with `datetime` variables.+ #' writeLines(c("x <- 2", "#second line comment", "x <- x + 2"), file_example) |
|
864 | +46 |
#' |
|
865 | +47 |
- #' Please read the official documentation for the `reticulate` package for additional+ #' read_script(file_example) |
|
866 | +48 |
- #' features and current limitations.+ read_script <- function(file, dataname = NULL) { |
|
867 | -+ | ||
49 | +2x |
- #'+ checkmate::assert_string(file) |
|
868 | -+ | ||
50 | +2x |
- #' @export+ checkmate::assert_file_exists(file) |
|
869 | -+ | ||
51 | +2x |
- #'+ paste( |
|
870 | -+ | ||
52 | +2x |
- #' @rdname python_dataset_connector+ code_exclude( |
|
871 | -+ | ||
53 | +2x |
- #'+ enclosed_with_dataname( |
|
872 | -+ | ||
54 | +2x |
- #' @examples+ get_code_single(file, read_sources = TRUE), |
|
873 | -+ | ||
55 | +2x |
- #' \dontrun{+ dataname = dataname |
|
874 | +56 |
- #' library(reticulate)+ ), |
|
875 | -+ | ||
57 | +2x |
- #'+ exclude_comments = TRUE |
|
876 | +58 |
- #' # supply python code directly in R+ ), |
|
877 | -+ | ||
59 | +2x |
- #'+ collapse = "\n" |
|
878 | +60 |
- #' x <- python_dataset_connector(+ ) |
|
879 | +61 |
- #' "ADSL",+ } |
|
880 | +62 |
- #' code = "import pandas as pd+ |
|
881 | +63 |
- #' data = pd.DataFrame({'STUDYID': [1, 2], 'USUBJID': [3, 4]})",+ #' Function to get a file out of a package |
|
882 | +64 |
- #' object = "data"+ #' |
|
883 | +65 |
- #' )+ #' @param pkg (`character`)\cr |
|
884 | +66 |
- #'+ #' The name of the package the file should be received from. |
|
885 | +67 |
- #' x$pull()+ #' @param file_name (`character`)\cr |
|
886 | +68 |
- #' x$get_raw_data()+ #' The name of the file to be received or path to it starting from |
|
887 | +69 |
- #'+ #' the base package path. |
|
888 | +70 |
- #' # supply an external python script+ #' @return The path to the file |
|
889 | +71 |
- #'+ #' @keywords internal |
|
890 | +72 |
- #' python_file <- tempfile(fileext = ".py")+ #' @examples |
|
891 | +73 |
- #' writeLines(+ #' teal.data:::get_package_file("teal.data", "WORDLIST") |
|
892 | +74 |
- #' text = "import pandas as pd+ #' teal.data:::get_package_file("teal.data", "cdisc_datasets/cdisc_datasets.yaml") |
|
893 | +75 |
- #' data = pd.DataFrame({'STUDYID': [1, 2], 'USUBJID': [3, 4]})",+ get_package_file <- function(pkg = NULL, file_name = NULL) { |
|
894 | -+ | ||
76 | +! |
- #' con = python_file+ checkmate::assert_string(pkg) |
|
895 | -+ | ||
77 | +! |
- #' )+ checkmate::assert_string(file_name) |
|
896 | -+ | ||
78 | +! |
- #'+ base_file <- system.file(file_name, package = pkg) |
|
897 | +79 |
- #' x <- python_dataset_connector(+ |
|
898 | -+ | ||
80 | +! |
- #' "ADSL",+ if (file.exists(base_file)) { |
|
899 | -+ | ||
81 | +! |
- #' file = python_file,+ return(base_file) |
|
900 | +82 |
- #' object = "data",+ } else { |
|
901 | -+ | ||
83 | +! |
- #' )+ stop(paste("There is no such file:", file_name, "or package:", pkg)) |
|
902 | +84 |
- #'+ } |
|
903 | +85 |
- #' x$pull()+ } |
|
904 | +86 |
- #' x$get_raw_data()+ |
|
905 | +87 |
- #'+ # Function to be used while trying to load the object of specific class from the script. |
|
906 | +88 |
- #' # supply pull `vars` from R+ object_file <- function(path, class) { |
|
907 | -+ | ||
89 | +6x |
- #'+ checkmate::assert_string(path) |
|
908 | -+ | ||
90 | +6x |
- #' y <- 8+ checkmate::assert_file_exists(path) |
- |
909 | -+ | ||
91 | +6x |
- #' x <- python_dataset_connector(+ checkmate::assert_string(class) |
|
910 | +92 |
- #' "ADSL",+ |
|
911 | -+ | ||
93 | +6x |
- #' code = "import pandas as pd+ lines <- paste0(readLines(path), collapse = "\n") |
|
912 | -+ | ||
94 | +6x |
- #' data = pd.DataFrame({'STUDYID': [r.y], 'USUBJID': [r.y]})",+ object <- eval(parse(text = lines, keep.source = FALSE)) |
|
913 | +95 |
- #' object = "data",+ |
|
914 | -+ | ||
96 | +6x |
- #' vars = list(y = y)+ if (!inherits(object, class)) { |
|
915 | -+ | ||
97 | +1x |
- #' )+ stop("The object returned from the file is not of ", class, " class.") |
|
916 | +98 |
- #'+ } |
|
917 | -+ | ||
99 | +5x |
- #' x$pull()+ return(object) |
|
918 | +100 |
- #' x$get_raw_data()+ } |
|
919 | +101 |
- #' }+ |
|
920 | +102 |
- python_dataset_connector <- function(dataname,+ #' Check if package can be loaded |
|
921 | +103 |
- file,+ #' |
|
922 | +104 |
- code,+ #' @param pckg `character` package name. |
|
923 | +105 |
- object = dataname,+ #' @param msg `character` error message to display if package is not available. |
|
924 | +106 |
- keys = character(0),+ #' |
|
925 | +107 |
- label = character(0),+ #' @return Error or invisible NULL. |
|
926 | +108 |
- mutate_code = character(0),+ #' @keywords internal |
|
927 | +109 |
- mutate_script = character(0),+ check_pkg_quietly <- function(pckg, msg) { |
|
928 | -+ | ||
110 | +14x |
- vars = list(),+ checkmate::assert_string(pckg) |
|
929 | -+ | ||
111 | +14x |
- metadata = NULL) {+ checkmate::assert_string(msg) |
|
930 | -! | +||
112 | +14x |
- if (!requireNamespace("reticulate", quietly = TRUE)) {+ if (!pckg %in% rownames(utils::installed.packages())) { |
|
931 | -! | +||
113 | +1x |
- stop("Cannot load package 'reticulate' - please install the package.", call. = FALSE)+ stop(msg) |
|
932 | +114 |
} |
|
933 | -! | +||
115 | +
- if (utils::packageVersion("reticulate") < 1.22) {+ |
||
934 | -! | +||
116 | +13x |
- stop("Please upgrade package 'reticulate', teal.data requires version >= 1.22")+ invisible(NULL) |
|
935 | +117 |
- }+ } |
|
936 | +118 | ||
937 | -! | -
- checkmate::assert_string(object)- |
- |
938 | -! | -
- if (!xor(missing(code), missing(file))) stop("Exactly one of 'code' and 'script' is required")- |
- |
939 | +119 | ||
940 | -! | +||
120 | +
- if (!missing(file)) {+ #' validate metadata as a list of length one atomic entries (or NULL) |
||
941 | -! | +||
121 | +
- checkmate::assert_string(file)+ #' @param metadata `object` to be checked |
||
942 | -! | +||
122 | +
- checkmate::assert_file_exists(file, extension = "py")+ #' @return `NULL` or throw error |
||
943 | -! | +||
123 | +
- x_fun <- CallablePythonCode$new("py_run_file") # nolint+ #' @examples |
||
944 | -! | +||
124 | +
- x_fun$set_args(list(file = file, local = TRUE))+ #' |
||
945 | +125 |
- } else {+ #' validate_metadata(NULL) |
|
946 | -! | +||
126 | +
- checkmate::assert_string(code)+ #' validate_metadata(list(A = TRUE, B = 10, C = "test")) |
||
947 | -! | +||
127 | +
- x_fun <- CallablePythonCode$new("py_run_string") # nolint+ #' \dontrun{ |
||
948 | -! | +||
128 | +
- x_fun$set_args(list(code = code, local = TRUE))+ #' validate_metadata(list(a = 1:10)) |
||
949 | +129 |
- }+ #' } |
|
950 | +130 |
-
+ #' |
|
951 | -! | +||
131 | +
- x_fun$set_object(object)+ #' @export |
||
952 | +132 |
-
+ validate_metadata <- function(metadata) { |
|
953 | -! | +||
133 | +720x |
- x <- dataset_connector(+ checkmate::assert_list(metadata, any.missing = FALSE, names = "named", null.ok = TRUE) |
|
954 | -! | +||
134 | +714x |
- dataname = dataname,+ lapply(names(metadata), function(name) { |
|
955 | -! | +||
135 | +156x |
- pull_callable = x_fun,+ checkmate::assert_atomic(metadata[[name]], len = 1, .var.name = name) |
|
956 | -! | +||
136 | +
- keys = keys,+ }) |
||
957 | -! | +||
137 | +710x |
- label = label,+ return(NULL) |
|
958 | -! | +||
138 | +
- code = code_from_script(mutate_code, mutate_script),+ } |
||
959 | -! | +||
139 | +
- vars = vars,+ |
||
960 | -! | +||
140 | +
- metadata = metadata+ #' Resolve the expected bootstrap theme |
||
961 | +141 |
- )+ #' @keywords internal |
|
962 | +142 |
-
+ get_teal_bs_theme <- function() { |
|
963 | -! | +||
143 | +8x |
- return(x)+ bs_theme <- getOption("teal.bs_theme") |
|
964 | -+ | ||
144 | +8x |
- }+ if (is.null(bs_theme)) { |
|
965 | -+ | ||
145 | +5x |
-
+ NULL |
|
966 | -+ | ||
146 | +3x |
- #' `Python` `CDISCTealDatasetConnector`+ } else if (!inherits(bs_theme, "bs_theme")) { |
|
967 | -+ | ||
147 | +2x |
- #'+ warning("teal.bs_theme has to be of a bslib::bs_theme class, the default shiny bootstrap is used.") |
|
968 | -+ | ||
148 | +2x |
- #' `r lifecycle::badge("experimental")`+ NULL |
|
969 | +149 |
- #' Create a `CDISCTealDatasetConnector` from `.py` file or through python code supplied directly.+ } else { |
|
970 | -+ | ||
150 | +1x |
- #'+ bs_theme |
|
971 | +151 |
- #' @inheritParams python_dataset_connector+ } |
|
972 | +152 |
- #' @inheritParams cdisc_dataset_connector+ } |
|
973 | +153 |
- #'+ |
|
974 | +154 |
- #' @export+ #' Format expression to string |
|
975 | +155 |
- #'+ #' Convert any expression to a single character vector |
|
976 | +156 |
- #' @rdname python_dataset_connector+ #' @param code (`language`, `expression`, `character`) |
|
977 | +157 |
- python_cdisc_dataset_connector <- function(dataname,+ #' @return `character(1)` |
|
978 | +158 |
- file,+ #' @keywords internal |
|
979 | +159 |
- code,+ format_expression <- function(code) { |
|
980 | -+ | ||
160 | +12x |
- object = dataname,+ if (is.language(code)) { |
|
981 | -+ | ||
161 | +2x |
- keys = get_cdisc_keys(dataname),+ code <- lang2calls(code) |
|
982 | +162 |
- parent = `if`(identical(dataname, "ADSL"), character(0L), "ADSL"),+ } |
|
983 | -+ | ||
163 | +12x |
- mutate_code = character(0),+ paste(code, collapse = "\n") |
|
984 | +164 |
- mutate_script = character(0),+ } |
|
985 | +165 |
- label = character(0),+ |
|
986 | +166 |
- vars = list(),+ |
|
987 | +167 |
- metadata = NULL) {- |
- |
988 | -! | -
- x <- python_dataset_connector(- |
- |
989 | -! | -
- dataname = dataname,+ # convert language object or lists of language objects to list of simple calls |
|
990 | -! | +||
168 | +
- file = file,+ # @param x `language` object or a list of thereof |
||
991 | -! | +||
169 | +
- code = code,+ # @return |
||
992 | -! | +||
170 | +
- object = object,+ # Given a `call`, an `expression`, a list of `call`s or a list of `expression`s, |
- ||
993 | -! | +||
171 | +
- keys = keys,+ # returns a list of `calls`. |
||
994 | -! | +||
172 | +
- mutate_code = mutate_code,+ # Symbols and atomic vectors (which may get mixed up in a list) are returned wrapped in list. |
||
995 | -! | +||
173 | +
- mutate_script = mutate_script,+ #' @keywords internal |
||
996 | -! | +||
174 | +
- label = label,+ lang2calls <- function(x) { |
||
997 | -! | +||
175 | +2x |
- vars = vars,+ if (is.atomic(x) || is.symbol(x)) { |
|
998 | +176 | ! |
- metadata = metadata+ return(list(x)) |
999 | +177 |
- )+ } |
|
1000 | -+ | ||
178 | +2x |
-
+ if (is.call(x)) { |
|
1001 | -! | +||
179 | +2x |
- res <- as_cdisc(+ if (identical(as.list(x)[[1L]], as.symbol("{"))) { |
|
1002 | -! | +||
180 | +1x |
- x,+ as.list(x)[-1L] |
|
1003 | -! | +||
181 | +
- parent = parent+ } else {+ |
+ ||
182 | +1x | +
+ list(x) |
|
1004 | +183 |
- )+ } |
|
1005 | +184 |
-
+ } else { |
|
1006 | +185 | ! |
- return(res)+ unlist(lapply(x, lang2calls), recursive = FALSE) |
1007 | +186 | ++ |
+ }+ |
+
187 |
}@@ -44967,14 +44518,14 @@ teal.data coverage - 74.87% |
1 |
- ## CallableFunction ====+ #' Data input for teal app |
||
3 |
- #' @title A \code{CallableFunction} class of objects+ #' @description `r lifecycle::badge("stable")` |
||
4 |
- #'+ #' Function is a wrapper around [teal_data()] and guesses `join_keys` |
||
5 |
- #' @description Object that stores a function name together with its arguments.+ #' for given datasets which names match ADAM datasets names. |
||
6 |
- #' Methods are then available to get the function call and evaluate it.+ #' |
||
7 |
- #'+ #' @inheritParams teal_data |
||
8 |
- #' @keywords internal+ #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr |
||
9 |
- #'+ #' (optional) object with datasets column names used for joining. |
||
10 |
- CallableFunction <- R6::R6Class( # nolint+ #' If empty then it would be automatically derived basing on intersection of datasets primary keys. |
||
11 |
- "CallableFunction",+ #' For ADAM datasets it would be automatically derived. |
||
12 |
- inherit = Callable,+ #' |
||
13 |
-
+ #' @return a `TealData` or `teal_data` object |
||
14 |
- ## __Public Methods ====+ #' |
||
15 |
- public = list(+ #' @details This function checks if there were keys added to all data sets |
||
16 |
- #' @description+ #' |
||
17 |
- #' Create a new \code{CallableFunction} object+ #' @export |
||
18 |
- #'+ #' |
||
19 |
- #' @param fun (\code{function})\cr+ #' @examples |
||
20 |
- #' function to be evaluated in class.+ #' |
||
21 |
- #' This is either a `function` object or its name as a string.+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
22 |
- #' @param env (\code{environment})\cr+ #' ADTTE <- teal.data::example_cdisc_data("ADTTE") |
||
23 |
- #' environment where function will be evaluated+ #' |
||
24 |
- #'+ #' cdisc_data( |
||
25 |
- #' @return new \code{CallableFunction} object+ #' ADSL = ADSL, |
||
26 |
- initialize = function(fun, env = new.env(parent = parent.env(globalenv()))) {+ #' ADTTE = ADTTE, |
||
27 | -211x | +
- super$initialize(env = env)+ #' code = quote({ |
|
28 | -211x | +
- if (missing(fun)) {+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
|
29 | -1x | +
- stop("A valid function name must be provided.")+ #' ADTTE <- teal.data::example_cdisc_data("ADTTE") |
|
30 |
- }+ #' }), |
||
31 | -210x | +
- if (!(checkmate::test_string(fun) || is.function(fun) || is.call(fun) || is.symbol(fun))) {+ #' join_keys = join_keys( |
|
32 | -1x | +
- stop("CallableFunction can be specified as character, symbol, call or function")+ #' join_key("ADSL", "ADTTE", c("STUDYID" = "STUDYID", "USUBJID" = "USUBJID")) |
|
33 |
- }+ #' ) |
||
34 |
-
+ #' ) |
||
35 | -208x | +
- fun_name <- private$get_callable_function(fun)+ cdisc_data <- function(..., |
|
36 | -203x | +
- private$fun_name <- deparse1(fun_name, collapse = "\n")+ join_keys = teal.data::cdisc_join_keys(...), |
|
37 |
-
+ code = "", |
||
38 | -203x | +
- private$refresh()+ check = FALSE) { |
|
39 | -+ | 21x |
-
+ data_objects <- list(...) |
40 | -203x | +21x |
- logger::log_trace("CallableFunction initialized with function: { deparse1(private$fun_name) }.")+ deprecated_join_keys_extract(data_objects, join_keys) |
41 | -+ | 20x |
-
+ teal_data(..., join_keys = join_keys, code = code, check = check) |
42 | -203x | +
- return(invisible(self))+ } |
|
43 |
- },+ |
||
44 |
- #' @description+ #' Extrapolate parents from `TealData` classes |
||
45 |
- #' get the arguments a function gets called with+ #' |
||
46 |
- #'+ #' `r lifecycle::badge("deprecated")` |
||
47 |
- #' @return arguments the function gets called with+ #' |
||
48 |
- get_args = function() {+ #' note: This function will be removed once the following classes are defunct: |
||
49 | -3x | +
- return(private$args)+ #' `TealDataConnector`, `TealDataset`, `TealDatasetConnector` |
|
50 |
- },+ #' |
||
51 |
- #' @description+ #' @keywords internal |
||
52 |
- #' Get function call with substituted arguments in \code{args}.+ deprecated_join_keys_extract <- function(data_objects, join_keys) { |
||
53 |
- #' These arguments will not be stored in the object.+ if ( |
||
54 | -+ | 21x |
- #'+ !checkmate::test_list( |
55 | -+ | 21x |
- #' @param deparse (\code{logical} value)\cr+ data_objects, |
56 | -+ | 21x |
- #' whether to return a deparsed version of call+ types = c("TealDataConnector", "TealDataset", "TealDatasetConnector") |
57 |
- #' @param args (\code{NULL} or named \code{list})\cr+ ) |
||
58 |
- #' dynamic arguments to function+ ) { |
||
59 | -+ | ! |
- #'+ return(join_keys) |
60 |
- #' @return \code{call} or \code{character} depending on \code{deparse} argument+ } |
||
61 |
- get_call = function(deparse = TRUE, args = NULL) {+ # TODO: check if redundant with same call in teal_data body |
||
62 | -479x | +21x |
- checkmate::assert_flag(deparse)+ update_join_keys_to_primary(data_objects, join_keys) |
63 | -479x | +
- checkmate::assert_list(args, names = "strict", min.len = 0, null.ok = TRUE)+ |
|
64 | -+ | 21x |
-
+ new_parents_fun <- function(data_objects) { |
65 | -479x | +25x |
- old_args <- private$args+ lapply( |
66 | -6x | +25x |
- if (length(args) > 0) self$set_args(args)+ data_objects, |
67 | -+ | 25x |
-
+ function(x) { |
68 | -479x | +47x |
- res <- if (deparse) {+ if (inherits(x, "TealDataConnector")) { |
69 | -28x | +4x |
- deparse1(private$call, collapse = "\n")+ unlist(new_parents_fun(x$get_items()), recursive = FALSE) |
70 |
- } else {+ } else { |
||
71 | -451x | +43x |
- private$call+ list( |
72 | -+ | 43x |
- }+ tryCatch( |
73 | -+ | 43x |
-
+ x$get_parent(), |
74 | -+ | 43x |
- # set args back to default+ error = function(cond) rep(character(0), length(x$get_datanames())) |
75 | -479x | +
- if (length(args) > 0) {+ ) |
|
76 | -6x | +
- lapply(names(args), self$set_arg_value, NULL)+ ) |
|
77 | -6x | +
- self$set_args(old_args)+ } |
|
79 |
-
+ ) |
||
80 | -479x | +
- return(res)+ } |
|
81 |
- },+ |
||
82 | -+ | 21x |
- #' @description+ new_parents <- unlist(new_parents_fun(data_objects), recursive = FALSE) |
83 |
- #' Set up function arguments+ |
||
84 | -+ | 21x |
- #'+ names(new_parents) <- unlist(lapply(data_objects, function(x) { |
85 | -+ | 42x |
- #' @param args (\code{NULL} or named \code{list})\cr+ if (inherits(x, "TealDataConnector")) { |
86 | -+ | 4x |
- #' function arguments to be stored persistently in the object. Setting \code{args} doesn't+ lapply(x$get_items(), function(y) y$get_dataname()) |
87 |
- #' remove other \code{args}, only create new of modify previous of the same name.+ } else { |
||
88 | -+ | 38x |
- #' To clean arguments specify \code{args = NULL}.+ x$get_datanames() |
89 |
- #'+ } |
||
90 |
- #' @return (`self`) invisibly for chaining.+ })) |
||
91 |
- set_args = function(args) {+ |
||
92 | -+ | 21x |
- # remove args if empty+ if (is_dag(new_parents)) { |
93 | -94x | +1x |
- if (length(args) == 0) {+ stop("Cycle detected in a parent and child dataset graph.") |
94 | -6x | +
- private$args <- NULL+ } |
|
95 | -6x | +20x |
- private$refresh()+ join_keys$set_parents(new_parents) |
96 | -6x | +20x |
- return(invisible(self))+ join_keys$update_keys_given_parents() |
97 |
- }+ |
||
98 | -88x | +20x |
- checkmate::assert_list(args, min.len = 0, names = "unique")+ join_keys |
99 |
-
+ } |
||
100 | -88x | +
- for (idx in seq_along(args)) {+ |
|
101 | -121x | +
- self$set_arg_value(+ #' Load `TealData` object from a file |
|
102 | -121x | +
- name = names(args)[[idx]],+ #' |
|
103 | -121x | +
- value = args[[idx]]+ #' @description `r lifecycle::badge("deprecated")` |
|
104 |
- )+ #' |
||
105 |
- }+ #' @inheritParams teal_data_file |
||
106 | -88x | +
- logger::log_trace(+ #' |
|
107 | -88x | +
- "CallableFunction$set_args args set for function: { deparse1(private$fun_name) }."+ #' @return `TealData` object |
|
108 |
- )+ #' |
||
109 |
-
+ #' @export |
||
110 | -88x | +
- return(invisible(self))+ #' |
|
111 |
- },+ #' @examples |
||
112 |
- #' @description+ #' file_example <- tempfile(fileext = ".R") |
||
113 |
- #' Set up single function argument with value+ #' writeLines( |
||
114 |
- #'+ #' text = c( |
||
115 |
- #' @param name (\code{character}) argument name+ #' "# code> |
||
116 |
- #' @param value argument value+ #' ADSL <- teal.data::example_cdisc_data('ADSL') |
||
117 |
- #'+ #' ADTTE <- teal.data::example_cdisc_data('ADTTE') |
||
118 |
- #' @return (`self`) invisibly for chaining.+ #' |
||
119 |
- set_arg_value = function(name, value) {+ #' cdisc_data( |
||
120 | -130x | +
- checkmate::assert_string(name)+ #' cdisc_dataset(\"ADSL\", ADSL), cdisc_dataset(\"ADTTE\", ADTTE), |
|
121 | -130x | +
- arg_names <- names(formals(eval(str2lang(private$fun_name))))+ #' code = \"ADSL <- teal.data::example_cdisc_data('ADSL') |
|
122 | -130x | +
- stopifnot(name %in% arg_names || "..." %in% arg_names || is.null(arg_names))+ #' ADTTE <- teal.data::example_cdisc_data('ADTTE')\", |
|
123 |
-
+ #' check = FALSE |
||
124 | -130x | +
- if (length(private$args) == 0) {+ #' ) |
|
125 | -80x | +
- private$args <- list()+ #' # <code" |
|
126 |
- }+ #' ), |
||
127 | -130x | +
- private$args[[name]] <- value+ #' con = file_example |
|
128 |
-
+ #' ) |
||
129 | -130x | +
- private$refresh()+ #' |
|
130 | -130x | +
- logger::log_trace("CallableFunction$set_arg_value args values set for arg: { deparse1(name) }.")+ #' cdisc_data_file(file_example) |
|
131 |
-
+ cdisc_data_file <- function(path, code = get_code(path)) { |
||
132 | -130x | +2x |
- return(invisible(self))+ lifecycle::deprecate_warn(when = "0.1.3", what = "cdisc_data_file()", with = "teal_data_file()") |
133 | +2x | +
+ object <- object_file(path, "TealData")+ |
+ |
134 | +2x | +
+ object$mutate(code)+ |
+ |
135 | +2x | +
+ return(object)+ |
+ |
136 |
- }+ } |
134 | +1 |
- ),+ ## CallablePythonCode ==== |
|
135 | +2 |
-
+ #' |
|
136 | +3 |
- ## __Private Fields ====+ #' @title A `CallablePythonCode` class of objects |
|
137 | +4 |
- private = list(+ #' @keywords internal |
|
138 | +5 |
- fun_name = character(0),+ #' |
|
139 | +6 |
- args = NULL, # named list with argument names and values+ CallablePythonCode <- R6::R6Class( # nolint |
|
140 | +7 |
- ## __Private Methods ====+ |
|
141 | +8 |
- # @description+ ## __Public Methods ==== |
|
142 | +9 |
- # Refresh call with function name and saved arguments+ classname = "CallablePythonCode", |
|
143 | +10 |
- #+ inherit = CallableFunction, |
|
144 | +11 |
- # @return nothing+ public = list( |
|
145 | +12 |
- refresh = function() {+ #' @description |
|
146 | -339x | +||
13 | +
- if (!is.null(private$fun_name) || !identical(private$fun_name, character(0))) {+ #' Create a new `CallablePythonCode` object |
||
147 | +14 |
- # replaced str2lang found at:+ #' |
|
148 | +15 |
- # https://rlang.r-lib.org/reference/call2.html+ #' @param fun (`function`)\cr |
|
149 | -339x | +||
16 | +
- private$call <- as.call(+ #' function to be evaluated in class. Function should be named |
||
150 | -339x | +||
17 | +
- c(rlang::parse_expr(private$fun_name), private$args)+ #' @param env (\code{environment})\cr |
||
151 | +18 |
- )+ #' environment where the result of python code evaluation are stored |
|
152 | +19 |
-
+ #' @return new `CallablePythonCode` object |
|
153 | +20 |
- # exception for source(...)$value+ initialize = function(fun, env = new.env(parent = parent.env(globalenv()))) { |
|
154 | -339x | +||
21 | +! |
- if (private$fun_name == "source") {+ if (!requireNamespace("reticulate", quietly = TRUE)) { |
|
155 | -9x | +||
22 | +! |
- private$call <- rlang::parse_expr(+ stop("Cannot load package 'reticulate' - please install the package.", call. = FALSE) |
|
156 | -9x | +||
23 | +
- sprintf("%s$value", deparse1(private$call, collapse = "\n"))+ }+ |
+ ||
24 | +! | +
+ if (utils::packageVersion("reticulate") < 1.22) {+ |
+ |
25 | +! | +
+ stop("Please upgrade package 'reticulate', teal.data requires version >= 1.22") |
|
157 | +26 |
- )+ } |
|
158 | -330x | +||
27 | +
- } else if (private$fun_name %in% c("py_run_file", "py_run_string")) {+ |
||
159 | +28 | ! |
- private$call <- rlang::parse_expr(+ super$initialize(fun = fun, env = env) |
160 | +29 | ! |
- sprintf("%s[[%s]]", deparse1(private$call, collapse = "\n"), deparse1(private$object, collapse = "\n"))+ logger::log_trace("CallablePythonCode initialized.")+ |
+
30 | +! | +
+ return(invisible(self)) |
|
161 | +31 |
- )+ }, |
|
162 | +32 |
- }+ #' @description |
|
163 | +33 |
- }+ #' For scripts and code that contain multiple objects, save the name |
|
164 | +34 |
- },+ #' of the object that corresponds to the final dataset of interest. |
|
165 | +35 |
- # @description+ #' This is required for running python scripts with `reticulate`. |
|
166 | +36 |
- # Returns a call to a function+ #' |
|
167 | +37 |
- #+ #' @param x (`character`) the name of the object produced by the code |
|
168 | +38 |
- # Returns the call to the function as defined in the enclosing environment.+ #' or script. |
|
169 | +39 |
- #+ #' |
|
170 | +40 |
- # @param callable \code{function, character, call, symbol} the function to return+ #' @return (`self`) invisibly for chaining. |
|
171 | +41 |
- #+ set_object = function(x) {+ |
+ |
42 | +! | +
+ private$object <- x+ |
+ |
43 | +! | +
+ private$refresh()+ |
+ |
44 | +! | +
+ logger::log_trace("CallablePythonCode$set_object object set.")+ |
+ |
45 | +! | +
+ return(invisible(self)) |
|
172 | +46 |
- # @return `call` the call to the function+ }, |
|
173 | +47 |
- #+ #' @description |
|
174 | +48 |
- get_callable_function = function(callable) {+ #' Execute `Callable` python code. |
|
175 | -208x | +||
49 | +
- if (is.character(callable) && private$is_prefixed_function(callable)) {+ #' |
||
176 | -11x | +||
50 | +
- private$get_call_from_prefixed_function(callable)+ #' @param args (`NULL` or named `list`)\cr |
||
177 | +51 |
- } else {+ #' supplied for callable functions only, these are dynamic arguments passed to |
|
178 | -197x | +||
52 | +
- private$get_call_from_symbol(callable)+ #' `reticulate::py_run_string` or `reticulate::py_run_file`. Dynamic arguments |
||
179 | +53 |
- }+ #' are executed in this call and are not saved which means that `self$get_call()` |
|
180 | +54 |
- },+ #' won't include them later. |
|
181 | +55 |
- # @param function_name (`character`) the function name prefixed with \code{::}+ #' @param try (`logical` value)\cr |
|
182 | +56 |
- # and the package name+ #' whether perform function evaluation inside `try` clause |
|
183 | +57 |
- # @return `call` the call to the function passed to this method+ #' |
|
184 | +58 |
- get_call_from_prefixed_function = function(function_name) {+ #' @return nothing or output from function depending on `return` |
|
185 | -11x | +||
59 | +
- package_function_names <- strsplit(function_name, "::")[[1]]+ #' argument. If `run` fails it will return object of class `simple-error` error |
||
186 | -11x | +||
60 | +
- fun <- get(package_function_names[2], envir = getNamespace(package_function_names[1]))+ #' when `try = TRUE` or will stop if `try = FALSE`. |
||
187 | -11x | +||
61 | +
- if (!is.function(fun)) {+ run = function(args = NULL, try = FALSE) { |
||
188 | -1x | +||
62 | +! |
- stop(sprintf("object '%s' of mode 'function' was not found", function_name))+ rlang::with_options(+ |
+ |
63 | +! | +
+ res <- super$run(args = args, try = try),+ |
+ |
64 | +! | +
+ reticulate.engine.environment = private$env |
|
189 | +65 | ++ |
+ )+ |
+
66 | +! | +
+ if (is.null(res)) {+ |
+ |
67 | +! | +
+ stop("The specified python object returned NULL or does not exist in the python code")+ |
+ |
68 |
} |
||
190 | -10x | +||
69 | +! | +
+ res+ |
+ |
70 | ++ |
+ }+ |
+ |
71 | ++ |
+ ),+ |
+ |
72 | ++ | + + | +|
73 | ++ |
+ ## __Private Fields ====+ |
+ |
74 | ++ |
+ private = list(+ |
+ |
75 | +
- str2lang(function_name)+ object = NULL, |
||
191 | +76 |
- },+ |
|
192 | +77 |
- # @param symbol (`function`, `symbol` or `character`) the item matching a function+ ## __Private Methods ==== |
|
193 | +78 |
- # @return `call` the call to the function passed to this method+ # @description |
|
194 | +79 |
- get_call_from_symbol = function(symbol) {+ # Refresh call with function name and saved arguments |
|
195 | -197x | +||
80 | +
- fun <- match.fun(symbol)+ # |
||
196 | -193x | +||
81 | +
- fun_environment <- environment(fun)+ # @return nothing |
||
197 | -193x | +||
82 | +
- if (isNamespace(fun_environment)) {+ refresh = function() { |
||
198 | -69x | +||
83 | +
- fun_name <- get_binding_name(fun, fun_environment)+ # replaced str2lang found at: |
||
199 | -69x | +||
84 | +
- namespace_name <- strsplit(rlang::env_name(fun_environment), ":")[[1]][2]+ # https://rlang.r-lib.org/reference/call2.html |
||
200 | -69x | +||
85 | +! |
- if (namespace_name != "base") {+ private$call <- as.call( |
|
201 | -8x | +||
86 | +! |
- fun_name <- paste(namespace_name, fun_name, sep = "::")+ c(rlang::parse_expr(private$fun_name), private$args) |
|
202 | +87 |
- }- |
- |
203 | -69x | -
- fun <- str2lang(fun_name)+ ) |
|
204 | +88 |
- }+ |
|
205 | -193x | +||
89 | +! |
- fun+ private$call <- rlang::parse_expr( |
|
206 | -+ | ||
90 | +! |
- },+ sprintf("%s[[%s]]", deparse1(private$call, collapse = "\n"), deparse1(private$object, collapse = "\n")) |
|
207 | +91 |
- # Checks whether a character vector is of this format+ ) |
|
208 | +92 |
- # <package_name>::<function_name>+ } |
|
209 | +93 |
- #+ ) |
|
210 | +94 |
- # @param function_name (`character`) the character vector+ ) |
|
211 | +95 |
- # @return `logical` `TRUE` if \code{function_name} is of the specified+ ## PythonCodeClass ==== |
|
212 | +96 |
- # format; `FALSE` otherwise+ #' |
|
213 | +97 |
- #+ #' @title A `CallablePythonCode` class of objects |
|
214 | +98 |
- is_prefixed_function = function(function_name) {- |
- |
215 | -18x | -
- grepl("^[[:ascii:]]+::[[:ascii:]]+$", function_name, perl = TRUE)+ #' @description `r lifecycle::badge("experimental")` |
|
216 | +99 |
- }+ #' |
|
217 | +100 |
- )+ PythonCodeClass <- R6::R6Class( # nolint |
|
218 | +101 |
- )+ classname = "PythonCodeClass", |
|
219 | +102 |
-
+ inherit = CodeClass, |
|
220 | +103 |
- ## Constructors ====+ |
|
221 | +104 |
-
+ ## __Public Methods ==== |
|
222 | +105 |
- #' Create \code{CallableFunction} object+ public = list( |
|
223 | +106 |
- #'+ #' @description |
|
224 | +107 |
- #' @description `r lifecycle::badge("stable")`+ #' Evaluates internal code within environment |
|
225 | +108 |
- #' Create \code{\link{CallableFunction}} object to execute specific function and get reproducible+ #' |
|
226 | +109 |
- #' call.+ #' @param vars (named `list`) additional pre-requisite vars to execute code |
|
227 | +110 |
- #'+ #' @param dataname (`character`) name of the data frame object to be returned |
|
228 | +111 |
- #' @param fun (\code{function})\cr+ #' @param envir (`environment`) environment in which code will be evaluated |
|
229 | +112 |
- #' any R function, directly by name or \code{character} string.+ #' |
|
230 | +113 |
- #'+ #' @return `data.frame` containing the mutated dataset |
|
231 | +114 |
- #' @return \code{CallableFunction} object+ eval = function(vars = list(), dataname = NULL, envir = new.env(parent = parent.env(.GlobalEnv))) { |
|
232 | -+ | ||
115 | +! |
- #'+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
233 | -+ | ||
116 | +! |
- #' @export+ execution_environment <- envir |
|
234 | +117 |
- #'+ |
|
235 | -+ | ||
118 | +! |
- #' @examples+ for (vars_idx in seq_along(vars)) { |
|
236 | -+ | ||
119 | +! |
- #' cf <- callable_function(fun = stats::median)+ var_name <- names(vars)[[vars_idx]] |
|
237 | -+ | ||
120 | +! |
- #' cf$set_args(list(x = 1:10, na.rm = FALSE))+ var_value <- vars[[vars_idx]] |
|
238 | -+ | ||
121 | +! |
- #' cf$run()+ if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) { |
|
239 | -+ | ||
122 | +! |
- #' cf$get_call()+ var_value <- get_raw_data(var_value) |
|
240 | +123 |
- callable_function <- function(fun) {+ } |
|
241 | -161x | +||
124 | +! |
- CallableFunction$new(fun)+ assign(envir = execution_environment, x = var_name, value = var_value) |
|
242 | +125 |
- }+ } |
|
243 | +126 | ||
244 | +127 |
- #' Gets the name of the binding+ # execute |
|
245 | -+ | ||
128 | +! |
- #'+ rlang::with_options( |
|
246 | -+ | ||
129 | +! |
- #' Gets the name of the object by finding its origin.+ super$eval(envir = execution_environment), |
|
247 | -+ | ||
130 | +! |
- #' Depending on type of object function uses different methods+ reticulate.engine.environment = execution_environment |
|
248 | +131 |
- #' to obtain original location. If no `env` is specified then+ ) |
|
249 | +132 |
- #' object is tracked by `substitute` along the `sys.frames`.+ |
|
250 | +133 |
- #' If `env` is specified then search is limited to specified+ # return early if only executing and not grabbing the dataset |
|
251 | -+ | ||
134 | +! |
- #' environment.\cr+ if (is.null(dataname)) { |
|
252 | -+ | ||
135 | +! |
- #'+ return(as.environment(as.list(execution_environment))) |
|
253 | +136 |
- #' @note+ } |
|
254 | +137 |
- #' Raises an error if the object is not found in the environment.+ |
|
255 | -+ | ||
138 | +! |
- #'+ if (!is.data.frame(execution_environment[[dataname]])) { |
|
256 | -+ | ||
139 | +! |
- #' @param object (R object)\cr+ out_msg <- sprintf( |
|
257 | -+ | ||
140 | +! |
- #' any R object+ "\n%s\n\n - Code from %s needs to return a data.frame assigned to an object of dataset name.", |
|
258 | -+ | ||
141 | +! |
- #' @param envir (`environment`)\cr+ self$get_code(), |
|
259 | -+ | ||
142 | +! |
- #' if origin of the object is known then should be provided for+ self$get_dataname() |
|
260 | +143 |
- #' more precise search+ ) |
|
261 | +144 |
- #' @return character+ + |
+ |
145 | +! | +
+ rlang::with_options(+ |
+ |
146 | +! | +
+ .expr = stop(out_msg, call. = FALSE),+ |
+ |
147 | +! | +
+ warning.length = max(min(8170, nchar(out_msg) + 30), 100) |
|
262 | +148 |
- #' @keywords internal+ ) |
|
263 | +149 |
- #'+ } |
|
264 | +150 |
- get_binding_name <- function(object, envir) {+ |
|
265 | -70x | +||
151 | +! |
- bindings_names <- ls(envir)+ new_set <- execution_environment[[dataname]] |
|
266 | -70x | +||
152 | +! |
- identical_binding_mask <- vapply(+ logger::log_trace("PythonCodeClass$eval successfuly evaluated the code.") |
|
267 | -70x | +||
153 | +
- bindings_names,+ |
||
268 | -70x | +||
154 | +! |
- function(binding_name) identical(get(binding_name, envir), object),+ return(new_set) |
|
269 | -70x | +||
155 | +
- FUN.VALUE = logical(1),+ } |
||
270 | -70x | +||
156 | +
- USE.NAMES = FALSE+ ) |
||
271 | +157 |
- )+ ) |
|
272 | -70x | +||
158 | +
- if (length(bindings_names[identical_binding_mask]) == 0) {+ |
||
273 | -1x | +||
159 | +
- stop("Object not found in the environment")+ #' Python Code |
||
274 | +160 |
- }+ #' |
|
275 | -69x | +||
161 | +
- bindings_names[identical_binding_mask]+ #' `r lifecycle::badge("experimental")` |
||
276 | +162 |
- }+ #' Create a python code object directly from python code or a |
1 | +163 |
- #' Load data from connection+ #' script containing python code. |
|
2 | +164 |
#' |
|
3 | +165 |
- #' @description `r lifecycle::badge("stable")`+ #' @details |
|
4 | +166 |
- #' Load data from connection. Function used on [`TealDatasetConnector`] and+ #' Used to mutate dataset connector objects with python code. See |
|
5 | +167 |
- #' [`TealDataset`] to obtain data from connection.+ #' [`mutate_dataset`] or [`mutate_data`] for details. |
|
6 | +168 |
#' |
|
7 | +169 |
- #' @param x (`TealDatasetConnector` or `TealDataset`)+ #' @param code (`character`)\cr |
|
8 | +170 |
- #' @param args (`NULL` or named `list`)\cr+ #' Code to mutate the dataset. Must contain the `dataset$dataname`. |
|
9 | +171 |
- #' additional dynamic arguments passed to function which loads the data.+ #' @param script (`character`)\cr |
|
10 | +172 |
- #' @param try (`logical`) whether perform function evaluation inside `try` clause+ #' file that contains python Code that can be read using `reticulate::py_run_script`. |
|
11 | +173 |
- #' @param conn Optional (`TealDataConnection`) object required to pull the data.+ #' |
|
12 | +174 |
- #' @param ... not used, only for support of S3+ #' @return (`PythonCodeClass`) object containing python code |
|
13 | +175 |
- #'+ #' @export |
|
14 | +176 |
- #' @return `x` with loaded `dataset` object+ #' |
|
15 | +177 |
- #' @export+ #' @examples |
|
16 | +178 |
- load_dataset <- function(x, ...) {- |
- |
17 | -121x | -
- UseMethod("load_dataset")+ #' \dontrun{ |
|
18 | +179 |
- }+ #' library(reticulate) |
|
19 | +180 |
-
+ #' library(magrittr) |
|
20 | +181 |
- #' @rdname load_dataset+ #' |
|
21 | +182 |
- #' @examples+ #' # mutate dataset object |
|
22 | +183 |
#' |
|
23 | +184 |
- #' # TealDataset --------+ #' random_data_connector <- function(dataname) { |
|
24 | +185 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ #' fun_dataset_connector( |
|
25 | +186 |
- #' ADSL_dataset <- dataset("ADSL", x = ADSL)+ #' dataname = dataname, |
|
26 | +187 |
- #'+ #' fun = teal.data::example_cdisc_data, |
|
27 | +188 |
- #' load_dataset(ADSL_dataset)+ #' fun_args = list(dataname = dataname), |
|
28 | +189 |
- #' @export+ #' ) |
|
29 | +190 |
- load_dataset.TealDataset <- function(x, ...) { # nolint+ #' } |
|
30 | -45x | +||
191 | +
- check_ellipsis(...)+ #' x <- random_data_connector(dataname = "ADSL") |
||
31 | -45x | +||
192 | +
- return(invisible(x$get_dataset()))+ #' |
||
32 | +193 |
- }+ #' x %>% mutate_dataset(python_code("import pandas as pd |
|
33 | +194 |
-
+ #' r.ADSL = pd.DataFrame({'x': [1]})")) |
|
34 | +195 |
- #' @rdname load_dataset+ #' |
|
35 | +196 |
- #' @examples+ #' x$get_code() |
|
36 | +197 |
- #'+ #' x$pull() |
|
37 | +198 |
- #' # TealDatasetConnector --------+ #' x$get_raw_data() |
|
38 | +199 |
#' |
|
39 | +200 |
- #' random_data_connector <- function(dataname) {+ #' # mutate data object |
|
40 | +201 |
- #' fun_dataset_connector(+ #' |
|
41 | +202 |
- #' dataname = dataname,+ #' y <- 8 |
|
42 | +203 |
- #' fun = teal.data::example_cdisc_data,+ #' tc <- cdisc_data( |
|
43 | +204 |
- #' fun_args = list(dataname = dataname),+ #' random_data_connector(dataname = "ADSL"), |
|
44 | +205 |
- #' )+ #' random_data_connector(dataname = "ADLB") |
|
45 | +206 |
- #' }+ #' ) |
|
46 | +207 |
#' |
|
47 | +208 |
- #' adsl <- random_data_connector(dataname = "ADSL")+ #' tc %>% mutate_data(python_code("import pandas as pd |
|
48 | +209 |
- #' load_dataset(adsl)+ #' r.ADSL = pd.DataFrame({'STUDYID': [r.y], 'USUBJID': [r.y]})"), vars = list(y = y)) |
|
49 | +210 |
- #' get_dataset(adsl)+ #' |
|
50 | +211 |
#' |
|
51 | +212 |
- #' adae <- random_data_connector(dataname = "ADAE")+ #' load_datasets(tc) # submit all |
|
52 | +213 |
- #' load_dataset(adae)+ #' ds <- tc$get_dataset("ADSL") |
|
53 | +214 |
- #' get_dataset(adae)+ #' ds$get_raw_data() |
|
54 | +215 |
- #' @export+ #' } |
|
55 | +216 |
- load_dataset.TealDatasetConnector <- function(x, args = NULL, try = FALSE, conn = NULL, ...) { # nolint+ python_code <- function(code = character(0), script = character(0)) { |
|
56 | -76x | +||
217 | +! |
- check_ellipsis(...)+ if (!xor(missing(code), missing(script))) stop("Exactly one of 'code' and 'script' is required") |
|
57 | -76x | +||
218 | +
- if (!is.null(conn)) {+ |
||
58 | +219 | ! |
- stopifnot(inherits(conn, "TealDataConnection"))+ if (length(script) > 0) {+ |
+
220 | +! | +
+ code <- deparse(call("py_run_file", script)) |
|
59 | +221 |
-
+ } else { |
|
60 | +222 | ! |
- conn$open()+ code <- deparse(call("py_run_string", code))+ |
+
223 | ++ |
+ } |
|
61 | +224 | ! |
- conn_obj <- conn$get_conn()+ py <- PythonCodeClass$new()+ |
+
225 | +! | +
+ py$set_code(code) |
|
62 | +226 | ||
63 | +227 | ! |
- x$get_pull_callable()$assign_to_env("conn", conn_obj)+ return(py) |
64 | +228 |
- }+ } |
65 | +1 |
-
+ ## TealData ==== |
|
66 | -76x | +||
2 | +
- x$pull(args = args, try = try)+ #' @title Manage multiple `TealDataConnector`, `TealDatasetConnector` and `TealDataset` objects. |
||
67 | +3 |
-
+ #' |
|
68 | -76x | +||
4 | +
- return(invisible(x))+ #' @description `r lifecycle::badge("experimental")` |
||
69 | +5 |
- }+ #' Class manages `TealDataConnector`, `TealDatasetConnector` and |
|
70 | +6 |
-
+ #' `TealDataset` objects and aggregate them in one collection. |
|
71 | +7 |
- #' Load datasets+ #' Class also decides whether to launch app before initialize teal application. |
|
72 | +8 |
#' |
|
73 | +9 |
- #' @description `r lifecycle::badge("stable")`+ #' @param ... (`TealDataConnector`, `TealDataset`, `TealDatasetConnector`)\cr |
|
74 | +10 |
- #'+ #' objects |
|
75 | +11 |
- #' @param x ([`TealData`], [`TealDataset`] or [`TealDatasetConnector`])+ #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr |
|
76 | +12 |
- #' @param args (`NULL` or named `list`)\cr+ #' (optional) object with dataset column relationships used for joining. |
|
77 | +13 |
- #' additional dynamic arguments passed to function which loads the data. Applicable only on [`TealDatasetConnector`])+ #' If empty then an empty `JoinKeys` object is passed by default. |
|
78 | +14 |
- #' @param try (`logical`)\cr+ #' @param check (`logical`) reproducibility check - whether evaluated preprocessing code gives the same objects |
|
79 | +15 |
- #' whether perform function evaluation inside `try` clause. Applicable only on [`TealDatasetConnector`])+ #' as provided in arguments. Check is run only if flag is true and preprocessing code is not empty. |
|
80 | +16 |
- #' @param ... (not used)\cr+ #' |
|
81 | +17 |
- #' only for support of S3+ #' @examples |
|
82 | +18 |
- #'+ #' adsl_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADSL")) |
|
83 | +19 |
- #' @export+ #' adlb_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADLB")) |
|
84 | +20 |
- #' @return If executed in the interactive session shiny app is opened to load the data. If executed in+ #' adrs_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADRS")) |
|
85 | +21 |
- #' shiny application - it returns shiny server module.+ #' adtte_cf <- callable_function(teal.data::example_cdisc_data)$set_args(list(dataname = "ADTTE")) |
|
86 | +22 |
- load_datasets <- function(x, ...) {+ #' x1 <- cdisc_dataset_connector("ADSL", adsl_cf, keys = get_cdisc_keys("ADSL")) |
|
87 | -! | +||
23 | +
- UseMethod("load_datasets")+ #' x2 <- cdisc_dataset_connector("ADRS", adrs_cf, keys = get_cdisc_keys("ADRS")) |
||
88 | +24 |
- }+ #' x3 <- cdisc_dataset( |
|
89 | +25 |
-
+ #' dataname = "ADAE", |
|
90 | +26 |
- #' @rdname load_datasets+ #' x = teal.data::example_cdisc_data("ADAE"), |
|
91 | +27 |
- #' @examples+ #' code = "library(teal.data)\nADAE <- teal.data::example_cdisc_data(\"ADAE\")" |
|
92 | +28 |
- #'+ #' ) |
|
93 | +29 |
- #' # TealDataset ------+ #' x4 <- cdisc_dataset_connector("ADTTE", adtte_cf, keys = get_cdisc_keys("ADTTE")) |
|
94 | +30 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ #' tc <- teal.data:::TealData$new(x1, x2, x3, x4) |
|
95 | +31 |
- #' x <- dataset("ADSL", x = ADSL)+ #' tc$get_datanames() |
|
96 | +32 |
- #'+ #' \dontrun{ |
|
97 | +33 |
- #' load_datasets(x)+ #' tc$launch() |
|
98 | +34 |
- #' @export+ #' get_datasets(tc) # equivalent to tc$get_datasets() |
|
99 | +35 |
- load_datasets.TealDataset <- function(x, ...) { # nolint+ #' tc$get_dataset("ADAE") |
|
100 | -! | +||
36 | +
- check_ellipsis(...)+ #' tc$check() |
||
101 | -! | +||
37 | +
- return(invisible(x$get_dataset()))+ #' } |
||
102 | +38 |
- }+ #' |
|
103 | +39 |
-
+ #' x <- cdisc_dataset( |
|
104 | +40 |
- #' @rdname load_datasets+ #' dataname = "ADSL", |
|
105 | +41 |
- #' @examples+ #' x = teal.data::example_cdisc_data("ADSL"), |
|
106 | +42 | ++ |
+ #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ |
+
43 | ++ |
+ #' )+ |
+ |
44 |
#' |
||
107 | +45 | ++ |
+ #' x2 <- cdisc_dataset_connector("ADTTE", adtte_cf, keys = get_cdisc_keys("ADTTE"))+ |
+
46 | ++ |
+ #' tc <- teal.data:::TealData$new(x, x2)+ |
+ |
47 |
- #' # TealDatasetConnector ------+ #' \dontrun{ |
||
108 | +48 |
- #' random_data_connector <- function(dataname) {+ #' # This errors as we have not pulled the data |
|
109 | +49 |
- #' fun_dataset_connector(+ #' # tc$get_datasets() |
|
110 | +50 |
- #' dataname = dataname,+ #' # pull the data and then we can get the datasets |
|
111 | +51 |
- #' fun = teal.data::example_cdisc_data,+ #' tc$launch() |
|
112 | +52 |
- #' fun_args = list(dataname = dataname),+ #' tc$get_datasets() |
|
113 | +53 |
- #' )+ #' get_raw_data(tc) |
|
114 | +54 |
#' } |
|
115 | +55 |
#' |
|
116 | +56 |
- #' adsl <- random_data_connector(dataname = "ADSL")+ TealData <- R6::R6Class( # nolint |
|
117 | +57 |
- #' load_datasets(adsl)+ classname = "TealData", |
|
118 | +58 |
- #' get_dataset(adsl)+ inherit = TealDataAbstract, |
|
119 | +59 |
- #'+ ## __Public Methods ==== |
|
120 | +60 |
- #' adae <- random_data_connector(dataname = "ADAE")+ public = list( |
|
121 | +61 |
- #' load_datasets(adae)+ #' @description |
|
122 | +62 |
- #' get_dataset(adae)+ #' Create a new object of `TealData` class |
|
123 | +63 |
- #' @export+ initialize = function(..., check = FALSE, join_keys = teal.data::join_keys()) { |
|
124 | -+ | ||
64 | +133x |
- load_datasets.TealDatasetConnector <- function(x, args = NULL, try = FALSE, ...) { # nolint+ checkmate::assert_class(join_keys, "JoinKeys") |
|
125 | -! | +||
65 | +
- check_ellipsis(...)+ |
||
126 | -! | +||
66 | +133x |
- x$pull(args = args, try = try)+ dot_args <- list(...) |
|
127 | -! | +||
67 | +133x |
- return(invisible(x))+ is_teal_data <- checkmate::test_list( |
|
128 | -+ | ||
68 | +133x |
- }+ dot_args, |
|
129 | -+ | ||
69 | +133x |
-
+ types = c("TealDataConnector", "TealDataset", "TealDatasetConnector") |
|
130 | +70 |
-
+ ) |
|
131 | -+ | ||
71 | +133x |
- #' @rdname load_datasets+ if (!all(is_teal_data)) { |
|
132 | -+ | ||
72 | +2x |
- #' @export+ stop("All elements should be of TealDataset(Connector) or TealDataConnector class") |
|
133 | +73 |
- #' @examples+ } |
|
134 | +74 |
- #'+ |
|
135 | -+ | ||
75 | +131x |
- #' # TealDataConnector --------+ datanames <- unlist(lapply(dot_args, get_dataname)) |
|
136 | -+ | ||
76 | +131x |
- #' random_data_connector <- function(dataname) {+ private$check_names(datanames) |
|
137 | +77 |
- #' fun_dataset_connector(+ |
|
138 | -+ | ||
78 | +130x |
- #' dataname = dataname,+ private$datasets <- dot_args |
|
139 | +79 |
- #' fun = teal.data::example_cdisc_data,+ |
|
140 | -+ | ||
80 | +130x |
- #' fun_args = list(dataname = dataname),+ self$set_check(check) |
|
141 | +81 |
- #' )+ |
|
142 | -+ | ||
82 | +130x |
- #' }+ private$pull_code <- CodeClass$new() |
|
143 | -+ | ||
83 | +130x |
- #'+ private$mutate_code <- CodeClass$new() |
|
144 | +84 |
- #' adsl <- random_data_connector(dataname = "ADSL")+ |
|
145 | -+ | ||
85 | +130x |
- #' adrs <- random_data_connector(dataname = "ADRS")+ private$join_keys <- join_keys |
|
146 | +86 |
- #'+ |
|
147 | -+ | ||
87 | +130x |
- #' rdc <- cdisc_data(adsl, adrs)+ self$id <- sample.int(1e11, 1, useHash = TRUE) |
|
148 | +88 |
- #' \dontrun{+ |
|
149 | -+ | ||
89 | +130x |
- #' load_datasets(rdc)+ logger::log_trace( |
|
150 | -+ | ||
90 | +130x |
- #' }+ "TealData initialized with data: { paste(self$get_datanames(), collapse = ' ') }." |
|
151 | +91 |
- load_datasets.TealDataConnector <- function(x, ...) { # nolint- |
- |
152 | -! | -
- check_ellipsis(...)+ ) |
|
153 | -! | +||
92 | +130x |
- if (interactive()) {+ return(invisible(self)) |
|
154 | -! | +||
93 | +
- x$launch()+ }, |
||
155 | +94 |
- } else {+ #' @description |
|
156 | -! | +||
95 | +
- return(invisible(x))+ #' Creates a copy of the object with keeping valid references |
||
157 | +96 |
- }+ #' between `TealDataset` and `TealDatasetConnector` objects |
|
158 | +97 |
- }+ #' @param deep (`logical(1)`)\cr |
|
159 | +98 |
-
+ #' argument passed to `clone` method. If `TRUE` deep copy is made |
|
160 | +99 |
- #' @rdname load_datasets+ #' @return self invisible |
|
161 | +100 |
- #' @export+ copy = function(deep = FALSE) { |
|
162 | -+ | ||
101 | +2x |
- #' @examples+ new_self <- self$clone(deep = deep) |
|
163 | -+ | ||
102 | +2x |
- #'+ new_self$reassign_datasets_vars() |
|
164 | -+ | ||
103 | +2x |
- #' # TealData --------+ logger::log_trace("TealData$copy{if (deep) ' deep-' else ' '}copied self.") |
|
165 | -+ | ||
104 | +2x |
- #' random_data_connector <- function(dataname) {+ invisible(new_self) |
|
166 | +105 |
- #' fun_dataset_connector(+ }, |
|
167 | +106 |
- #' dataname = dataname,+ #' @description |
|
168 | +107 |
- #' fun = teal.data::example_cdisc_data,+ #' Prints this `TealData`. |
|
169 | +108 |
- #' fun_args = list(dataname = dataname),+ #' |
|
170 | +109 |
- #' )+ #' @param ... additional arguments to the printing method |
|
171 | +110 |
- #' }+ #' @return invisibly self |
|
172 | +111 |
- #'+ print = function(...) { |
|
173 | -+ | ||
112 | +1x |
- #' adsl <- random_data_connector(dataname = "ADSL")+ check_ellipsis(...) |
|
174 | +113 |
- #' adlb <- random_data_connector(dataname = "ADLB")+ |
|
175 | -+ | ||
114 | +1x |
- #' adrs <- random_data_connector(dataname = "ADRS")+ cat(sprintf( |
|
176 | -+ | ||
115 | +1x |
- #'+ "A %s object containing %d TealDataset/TealDatasetConnector object(s) as element(s):\n", |
|
177 | -+ | ||
116 | +1x |
- #' tc <- cdisc_data(adsl, adlb, adrs)+ class(self)[1], |
|
178 | -+ | ||
117 | +1x |
- #' \dontrun{+ length(private$datasets) |
|
179 | +118 |
- #' load_datasets(tc)+ )) |
|
180 | +119 |
- #' }+ |
|
181 | -+ | ||
120 | +1x |
- load_datasets.TealData <- function(x, ...) { # nolint+ for (i in seq_along(private$datasets)) { |
|
182 | -! | +||
121 | +2x |
- check_ellipsis(...)+ cat(sprintf("--> Element %d:\n", i)) |
|
183 | -! | +||
122 | +2x |
- if (interactive()) {+ print(private$datasets[[i]]) |
|
184 | -! | +||
123 | +
- x$launch()+ } |
||
185 | +124 |
- } else {+ |
|
186 | -! | +||
125 | +1x |
- return(invisible(x))+ invisible(self) |
|
187 | +126 |
- }+ }, |
|
188 | +127 |
- }+ # ___ getters ==== |
1 | +128 |
- #' Get Label Attributes of Variables in a \code{data.frame}+ #' @description |
||
2 | +129 |
- #'+ #' Get data connectors. |
||
3 | +130 |
- #' Variable labels can be stored as a \code{label} attribute for each variable.+ #' |
||
4 | +131 |
- #' This functions returns a named character vector with the variable labels+ #' @return (`list`) with all `TealDatasetConnector` or `TealDataConnector` objects. |
||
5 | +132 |
- #' (empty sting if not specified)+ get_connectors = function() {+ |
+ ||
133 | +5x | +
+ return(Filter(+ |
+ ||
134 | +5x | +
+ function(x) {+ |
+ ||
135 | +9x | +
+ inherits(x, "TealDatasetConnector") || inherits(x, "TealDataConnector") |
||
6 | +136 |
- #'+ }, |
||
7 | -+ | |||
137 | +5x |
- #' @param x a \code{data.frame} object+ private$datasets |
||
8 | +138 |
- #' @param fill boolean in case the \code{label} attribute does not exist if+ )) |
||
9 | +139 |
- #' \code{TRUE} the variable names is returned, otherwise \code{NA}+ }, |
||
10 | +140 |
- #'+ #' @description |
||
11 | +141 |
- #' @source This function was taken 1-1 from+ #' Get all datasets and all dataset connectors |
||
12 | +142 |
- #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of+ #' |
||
13 | +143 |
- #' the dependency tree.+ #' @param dataname (`character` value)\cr |
||
14 | +144 |
- #'+ #' name of dataset connector to be returned. If `NULL`, all connectors are returned. |
||
15 | +145 |
- #' @seealso [col_relabel()] [`col_labels<-`]+ #' |
||
16 | +146 |
- #'+ #' @return `list` with all datasets and all connectors |
||
17 | +147 |
- #' @return a named character vector with the variable labels, the names+ get_items = function(dataname = NULL) { |
||
18 | -+ | |||
148 | +105x |
- #' correspond to the variable names+ checkmate::assert_string(dataname, null.ok = TRUE) |
||
19 | +149 |
- #'+ |
||
20 | -+ | |||
150 | +105x |
- #' @export+ get_sets <- function(x) { |
||
21 | -+ | |||
151 | +195x |
- #'+ if (inherits(x, "TealDataConnector")) { |
||
22 | -+ | |||
152 | +12x |
- #' @examples+ x$get_items() |
||
23 | +153 |
- #' x <- iris+ } else { |
||
24 | -+ | |||
154 | +183x |
- #' col_labels(x)+ x |
||
25 | +155 |
- #' col_labels(x) <- paste("label for", names(iris))+ } |
||
26 | +156 |
- #' col_labels(x)+ } |
||
27 | +157 |
- col_labels <- function(x, fill = FALSE) {+ |
||
28 | -1x | +158 | +105x |
- stopifnot(is.data.frame(x))+ sets <- unlist(lapply(private$datasets, get_sets)) |
29 | -1x | -
- if (NCOL(x) == 0) {- |
- ||
30 | -! | +159 | +105x |
- return(character())+ names(sets) <- vapply(sets, get_dataname, character(1)) |
31 | +160 |
- }+ |
||
32 | -+ | |||
161 | +104x |
-
+ if (checkmate::test_string(dataname)) { |
||
33 | -1x | +162 | +5x |
- y <- Map(function(col, colname) {+ if (!(dataname %in% self$get_datanames())) { |
34 | -4x | +163 | +2x |
- label <- attr(col, "label")+ stop(paste("dataset", dataname, "not found")) |
35 | +164 | - - | -||
36 | -4x | -
- if (is.null(label)) {+ } |
||
37 | -4x | -
- if (fill) {- |
- ||
38 | -! | +165 | +3x |
- colname+ return(sets[[dataname]]) |
39 | +166 |
} else { |
||
40 | -1x | +167 | +99x |
- NA_character_+ return(sets) |
41 | +168 |
} |
||
42 | +169 |
- } else {+ }, |
||
43 | -! | +|||
170 | +
- if (!is.character(label) && !(length(label) == 1)) {+ |
|||
44 | -! | +|||
171 | +
- stop("label for variable ", colname, "is not a character string")+ #' @description |
|||
45 | +172 |
- }+ #' Get join keys between two datasets. |
||
46 | -! | +|||
173 | +
- as.vector(label)+ #' |
|||
47 | +174 |
- }+ #' @param dataset_1 (`character`) name of first dataset. |
||
48 | -1x | +|||
175 | +
- }, x, colnames(x))+ #' @param dataset_2 (`character`) name of second dataset. |
|||
49 | +176 |
-
+ #' @return (`character`) named character vector x with names(x) the |
||
50 | -1x | +|||
177 | +
- labels <- unlist(y, recursive = FALSE, use.names = TRUE)+ #' columns of `dataset_1` and the values of `(x)` the corresponding join |
|||
51 | +178 |
-
+ #' keys in `dataset_2` or `character(0)` if no relationship |
||
52 | -1x | +|||
179 | +
- if (!is.character(labels)) {+ get_join_keys = function(dataset_1, dataset_2) { |
|||
53 | -! | +|||
180 | +179x |
- stop("label extraction failed")+ if (missing(dataset_1) && missing(dataset_2)) { |
||
54 | -+ | |||
181 | +16x |
- }+ private$join_keys |
||
55 | +182 |
-
+ } else { |
||
56 | -1x | +183 | +163x |
- labels+ private$join_keys$get(dataset_1, dataset_2) |
57 | +184 |
- }+ } |
||
58 | +185 |
-
+ }, |
||
59 | +186 |
- #' Set Label Attributes of All Variables in a \code{data.frame}+ |
||
60 | +187 |
- #'+ #' @description |
||
61 | +188 |
- #' Variable labels can be stored as a \code{label} attribute for each variable.+ #' returns the parents list of the datasets. |
||
62 | +189 |
- #' This functions sets all non-missing (non-NA) variable labels in a \code{data.frame}+ #' |
||
63 | +190 |
- #'+ #' @return named (`list`) of the parents of all datasets. |
||
64 | +191 |
- #' @inheritParams col_labels+ get_parents = function() {+ |
+ ||
192 | +1x | +
+ private$join_keys$get_parents() |
||
65 | +193 |
- #' @param value new variable labels, \code{NA} removes the variable label+ }, |
||
66 | +194 |
- #'+ |
||
67 | +195 |
- #' @source This function was taken 1-1 from+ # ___ shiny ==== |
||
68 | +196 |
- #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of+ |
||
69 | +197 |
- #' the dependency tree.+ #' @description |
||
70 | +198 |
- #'+ #' |
||
71 | +199 |
- #' @seealso [col_labels()] [col_relabel()]+ #' Get a shiny-module UI to render the necessary app to |
||
72 | +200 |
- #'+ #' derive `TealDataConnector` object's data |
||
73 | +201 |
- #' @return modifies the variable labels of \code{x}+ #' |
||
74 | +202 |
- #'+ #' @param id (`character`) item ID for the shiny module |
||
75 | +203 |
- #' @export+ #' @return the `shiny` `ui` function |
||
76 | +204 |
- #'+ get_ui = function(id) {+ |
+ ||
205 | +4x | +
+ if (is.null(private$ui)) {+ |
+ ||
206 | +! | +
+ div(id = id, "Data Loaded") |
||
77 | +207 |
- #' @examples+ } else {+ |
+ ||
208 | +4x | +
+ private$ui(id) |
||
78 | +209 |
- #' x <- iris+ } |
||
79 | +210 |
- #' col_labels(x)+ }, |
||
80 | +211 |
- #' col_labels(x) <- paste("label for", names(iris))+ #' @description |
||
81 | +212 |
- #' col_labels(x)+ #' |
||
82 | +213 |
- #'+ #' Get a shiny-module server to render the necessary app to |
||
83 | +214 |
- #' if (interactive()) {+ #' derive `TealDataConnector` object's data |
||
84 | +215 |
- #' View(x) # in RStudio data viewer labels are displayed+ #' |
||
85 | +216 |
- #' }+ #' @return `shiny` server module. |
||
86 | +217 |
- `col_labels<-` <- function(x, value) {+ get_server = function() { |
||
87 | -6x | +|||
218 | +! |
- stopifnot(+ if (is.null(private$server)) { |
||
88 | -6x | +|||
219 | +! |
- is.data.frame(x),+ return( |
||
89 | -6x | +|||
220 | +! |
- is.character(value),+ function(id) { |
||
90 | -6x | +|||
221 | +! |
- ncol(x) == length(value)+ moduleServer( |
||
91 | -+ | |||
222 | +! |
- )+ id = id, |
||
92 | -+ | |||
223 | +! |
-
+ module = function(input, output, session) { |
||
93 | -6x | +|||
224 | +! |
- theseq <- if (!is.null(names(value))) names(value) else seq_along(x)+ reactive(self) |
||
94 | +225 |
- # across columns of x+ } |
||
95 | -6x | +|||
226 | +
- for (j in theseq) {+ ) |
|||
96 | -26x | +|||
227 | +
- attr(x[[j]], "label") <- if (!is.na(value[j])) {- |
- |||
97 | -26x | +|||
228 | +
- value[j]+ ) |
|||
98 | +229 |
- } else {+ } else { |
||
99 | +230 | ! |
- NULL+ function(id) { |
|
100 | -+ | |||
231 | +! |
- }+ moduleServer( |
||
101 | -+ | |||
232 | +! |
- }+ id = id, |
||
102 | -+ | |||
233 | +! |
-
+ module = private$server |
||
103 | -6x | +|||
234 | +
- x+ ) |
|||
104 | +235 |
- }+ } |
||
105 | +236 |
-
+ } |
||
106 | +237 |
- #' Copy and Change Variable Labels of a \code{data.frame}+ }, |
||
107 | +238 |
- #'+ #' @description |
||
108 | +239 |
- #' Relabel a subset of the variables+ #' |
||
109 | +240 |
- #'+ #' Launch an app that allows to run the user-interfaces of all |
||
110 | +241 |
- #' @inheritParams col_labels<-+ #' `TealDataConnector` and `TealDatasetConnector` modules |
||
111 | +242 |
- #' @param ... name-value pairs, where name corresponds to a variable name in+ #' |
||
112 | +243 |
- #' \code{x} and the value to the new variable label+ #' This piece is mainly used for debugging. |
||
113 | +244 |
- #'+ launch = function() { |
||
114 | +245 |
- #' @return a copy of \code{x} with changed labels according to \code{...}+ # if no data connectors can append any dataset connectors |
||
115 | +246 |
- #'+ # and not load an app |
||
116 | -+ | |||
247 | +! |
- #' @source This function was taken 1-1 from+ if (self$is_pulled()) { |
||
117 | -+ | |||
248 | +! |
- #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of+ stop("All the datasets have already been pulled.") |
||
118 | +249 |
- #' the dependency tree.+ } |
||
119 | +250 |
- #'+ |
||
120 | +251 |
- #' @seealso [col_labels()] [`col_labels<-`]+ # otherwise load TealDataConnector and |
||
121 | +252 |
- #'+ # TealDatasetConnector with shiny app |
||
122 | -+ | |||
253 | +! |
- #' @export+ shinyApp( |
||
123 | -+ | |||
254 | +! |
- #'+ ui = fluidPage( |
||
124 | -+ | |||
255 | +! |
- #' @examples+ theme = get_teal_bs_theme(), |
||
125 | -+ | |||
256 | +! |
- #' x <- col_relabel(iris, Sepal.Length = "Sepal Length of iris flower")+ fluidRow( |
||
126 | -+ | |||
257 | +! |
- #' col_labels(x)+ column( |
||
127 | -+ | |||
258 | +! |
- #'+ width = 8, |
||
128 | -+ | |||
259 | +! |
- col_relabel <- function(x, ...) {+ offset = 2, |
||
129 | +260 | ! |
- stopifnot(is.data.frame(x))+ self$get_ui(id = "main_app"), |
|
130 | +261 | ! |
- if (missing(...)) {+ shinyjs::hidden( |
|
131 | +262 | ! |
- return(x)+ tags$div( |
|
132 | -+ | |||
263 | +! |
- }+ id = "data_loaded", |
||
133 | +264 | ! |
- dots <- list(...)+ div( |
|
134 | +265 | ! |
- varnames <- names(dots)+ h3("Data successfully loaded."), |
|
135 | +266 | ! |
- stopifnot(!is.null(varnames))+ p("You can close this window and get back to R console.") |
|
136 | +267 |
-
+ ) |
||
137 | -! | +|||
268 | +
- map_varnames <- match(varnames, colnames(x))+ ) |
|||
138 | +269 |
-
+ ), |
||
139 | +270 | ! |
- if (any(is.na(map_varnames))) {+ include_js_files(), |
|
140 | +271 | ! |
- stop("variables: ", paste(varnames[is.na(map_varnames)], collapse = ", "), " not found")+ br() |
|
141 | +272 |
- }+ ) |
||
142 | +273 |
-
+ )+ |
+ ||
274 | ++ |
+ ), |
||
143 | +275 | ! |
- if (any(vapply(dots, Negate(is.character), logical(1)))) {+ server = function(input, output, session) { |
|
144 | +276 | ! |
- stop("all variable labels must be of type character")+ session$onSessionEnded(stopApp) |
|
145 | -+ | |||
277 | +! |
- }+ dat <- self$get_server()(id = "main_app") |
||
146 | +278 | |||
147 | +279 | ! |
- for (i in seq_along(map_varnames)) {+ observeEvent(dat(), { |
|
148 | +280 | ! |
- attr(x[[map_varnames[[i]]]], "label") <- dots[[i]]+ if (self$is_pulled()) {+ |
+ |
281 | +! | +
+ shinyjs::show("data_loaded")+ |
+ ||
282 | +! | +
+ stopApp() |
||
149 | +283 |
- }+ } |
||
150 | +284 |
-
+ }) |
||
151 | +285 | ! |
- x+ NULL |
|
152 | +286 |
- }+ } |
1 | +287 |
- ## `CodeClass` ====+ ) |
||
2 | +288 |
- #'+ }, |
||
3 | +289 |
- #' @title Code Class+ |
||
4 | +290 |
- #' @keywords internal+ # ___ mutate ==== |
||
5 | +291 |
- #'+ #' @description |
||
6 | +292 |
- #' @examples+ #' Change join_keys for a given pair of dataset names |
||
7 | +293 |
- #' cc <- teal.data:::CodeClass$new()+ #' @param dataset_1,dataset_2 (`character`) datasets for which join_keys are to be returned |
||
8 | +294 |
- #' cc$set_code(c("foo <- function() {1}", "foo2 <- function() {2}"))+ #' @param val (named `character`) column names used to join |
||
9 | +295 |
- #' cc$get_code()+ #' @return (`self`) invisibly for chaining |
||
10 | +296 |
- #' cc$get_code(deparse = FALSE)+ mutate_join_keys = function(dataset_1, dataset_2, val) {+ |
+ ||
297 | +3x | +
+ private$join_keys$mutate(dataset_1, dataset_2, val) |
||
11 | +298 |
- #'+ }, |
||
12 | +299 |
- #' cc$set_code(c("DF <- data.frame(x = 1:10)", "DF$y <- 1"), "DF")+ |
||
13 | +300 |
- #' cc$set_code("DF$a <- foo()", "DF")+ # ___ check ==== |
||
14 | +301 |
- #'+ #' @description |
||
15 | +302 |
- #' # dependent dataset+ #' Check there is consistency between the datasets and join_keys |
||
16 | +303 |
- #' cc$set_code(c("DF2 <- data.frame(x2 = 1:10)", "DF2$y2 <- DF$y"), "DF2", deps = "DF")+ #' @return raise and error or invisible `TRUE` |
||
17 | +304 |
- #'+ check_metadata = function() {+ |
+ ||
305 | +52x | +
+ if (isFALSE(self$is_pulled())) { |
||
18 | +306 |
- #' cc$set_code("baz <- function() {2}")+ # all the checks below required data to be already pulled+ |
+ ||
307 | +4x | +
+ return(invisible(TRUE)) |
||
19 | +308 |
- #' cc$set_code("DF2$a <- baz()", "DF2")+ } |
||
20 | +309 |
- #'+ + |
+ ||
310 | +48x | +
+ for (dataset in self$get_datasets()) {+ |
+ ||
311 | +82x | +
+ dataname <- get_dataname(dataset)+ |
+ ||
312 | +82x | +
+ dataset_colnames <- dataset$get_colnames() |
||
21 | +313 |
- #' cc$get_code()+ |
||
22 | +314 |
- #' cc$get_code("DF")+ # expected columns in this dataset from JoinKeys specification+ |
+ ||
315 | +82x | +
+ join_key_cols <- unique(unlist(lapply(self$get_join_keys(dataname), names)))+ |
+ ||
316 | +82x | +
+ if (!is.null(join_key_cols) && !all(join_key_cols %in% dataset_colnames)) {+ |
+ ||
317 | +3x | +
+ stop(+ |
+ ||
318 | +3x | +
+ paste(+ |
+ ||
319 | +3x | +
+ "The join key specification requires dataset",+ |
+ ||
320 | +3x | +
+ dataname,+ |
+ ||
321 | +3x | +
+ "to contain the following columns:",+ |
+ ||
322 | +3x | +
+ paste(join_key_cols, collapse = ", ") |
||
23 | +323 |
- #' cc$get_code("DF2")+ ) |
||
24 | +324 |
- #'+ ) |
||
25 | +325 |
- #'+ } |
||
26 | +326 |
- #' x1 <- teal.data:::CodeClass$new()+ |
||
27 | +327 |
- #' x1$set_code("DF <- data.frame(x = 1:10)", "DF")+ # check if primary keys in dataset |
||
28 | -+ | |||
328 | +79x |
- #' x1$get_code()+ primary_key_cols <- self$get_join_keys(dataname, dataname) |
||
29 | -+ | |||
329 | +79x |
- #'+ if (!is.null(primary_key_cols) && !all(primary_key_cols %in% dataset_colnames)) { |
||
30 | -+ | |||
330 | +! |
- #' x2 <- teal.data:::CodeClass$new()+ stop( |
||
31 | -+ | |||
331 | +! |
- #' x2$set_code(c("DF2 <- data.frame(x2 = 1:10)", "DF2$x2 <- DF$x"), "DF2", deps = "DF")+ paste( |
||
32 | -+ | |||
332 | +! |
- #' x2$get_code()+ "The primary keys specification requires dataset", |
||
33 | -+ | |||
333 | +! |
- #'+ dataname, |
||
34 | -+ | |||
334 | +! |
- #' x <- teal.data:::CodeClass$new()+ "to contain the following columns:", |
||
35 | -+ | |||
335 | +! |
- #' x$append(x1)+ paste(primary_key_cols, collapse = ", ") |
||
36 | +336 |
- #' x$append(x2)+ ) |
||
37 | +337 |
- #'+ ) |
||
38 | +338 |
- #' x$get_code()+ } |
||
39 | -+ | |||
339 | +79x |
- #' x$get_code("DF")+ dataset$check_keys() |
||
40 | +340 |
- #' x$get_code("DF2")+ } |
||
41 | +341 |
- #' x$get_code(c("DF", "DF2"))+ |
||
42 | -+ | |||
342 | +43x |
- #'+ logger::log_trace("TealData$check_metadata metadata check passed.") |
||
43 | +343 |
- #' x3 <- teal.data:::CodeClass$new()+ |
||
44 | -+ | |||
344 | +43x |
- #' x3$set_code("DF3 <- data.frame(x3 = 1:10) ", "DF3")+ return(invisible(TRUE)) |
||
45 | +345 |
- #' x3$get_code()+ } |
||
46 | +346 |
- #'+ ), |
||
47 | +347 |
- #' x$append(x3)+ |
||
48 | +348 |
- #' x$get_code("DF3")+ ## __Private Fields ==== |
||
49 | +349 |
- #'+ private = list( |
||
50 | +350 |
- #' # mutation simulation+ join_keys = NULL, |
||
51 | +351 |
- #' x$set_code("DF3$x <- foo(DF$x)", "DF3", deps = "DF")+ ui = function(id) { |
||
52 | -+ | |||
352 | +4x |
- #' x$get_code("DF3")+ ns <- NS(id) |
||
53 | +353 |
- CodeClass <- R6::R6Class( # nolint+ |
||
54 | +354 |
- "CodeClass",+ # connectors ui(s) + submit button |
||
55 | -+ | |||
355 | +4x |
- ## __Public Methods ====+ fluidPage( |
||
56 | -+ | |||
356 | +4x |
- public = list(+ include_js_files(), |
||
57 | -+ | |||
357 | +4x |
- #' @description+ theme = get_teal_bs_theme(), |
||
58 | -+ | |||
358 | +4x |
- #' `CodeClass` constructor+ shinyjs::hidden( |
||
59 | -+ | |||
359 | +4x |
- #' @param code (`character`) vector of code text to be set+ column( |
||
60 | -+ | |||
360 | +4x |
- #' @param dataname optional, (`character`) vector of `datanames` to assign code to. If empty then the code+ id = ns("delayed_data"), |
||
61 | -+ | |||
361 | +4x |
- #' is considered to be "global"+ width = 8, |
||
62 | -+ | |||
362 | +4x |
- #' @param deps optional, (`character`) vector of `datanames` that given code depends on+ offset = 2, |
||
63 | -+ | |||
363 | +4x |
- #' @return object of class `CodeClass`+ div( |
||
64 | -+ | |||
364 | +4x |
- initialize = function(code = character(0), dataname = character(0), deps = character(0)) {+ tagList( |
||
65 | -3518x | +365 | +4x |
- if (length(code) > 0) {+ lapply( |
66 | -13x | +366 | +4x |
- self$set_code(code, dataname, deps)+ private$datasets, |
67 | -+ | |||
367 | +4x |
- }+ function(x) { |
||
68 | -3518x | +368 | +6x |
- logger::log_trace("CodeClass initialized.")+ div( |
69 | -3518x | +369 | +6x |
- return(invisible(self))+ if (inherits(x, "TealDataConnector")) { |
70 | -+ | |||
370 | +! |
- },+ ui <- x$get_ui(id = ns(x$id)) |
||
71 | -+ | |||
371 | +! |
- #' @description+ if (is.null(ui)) { |
||
72 | -+ | |||
372 | +! |
- #' Append `CodeClass` object to a given `CodeClass` object+ ui <- div( |
||
73 | -+ | |||
373 | +! |
- #' @param x (`CodeClass`) object to be appended+ h4("TealDataset Connector for: ", lapply(x$get_datanames(), code)),+ |
+ ||
374 | +! | +
+ p(icon("check"), "Ready to Load") |
||
74 | +375 |
- #' @return changed `CodeClass` object+ ) |
||
75 | +376 |
- append = function(x) {+ } |
||
76 | -3022x | +|||
377 | +! |
- stopifnot(inherits(x, "CodeClass"))+ ui |
||
77 | -3022x | +378 | +6x |
- if (length(x$code) > 0) {+ } else if (inherits(x, "TealDatasetConnector")) { |
78 | -1458x | +|||
379 | +! |
- for (code_i in x$code) {+ ui <- x$get_ui(id = ns(paste0(x$get_datanames(), collapse = "_"))) |
||
79 | -2782x | +|||
380 | +! |
- private$set_code_single(code_i)+ if (is.null(ui)) { |
||
80 | -+ | |||
381 | +! |
- }+ ui <- div( |
||
81 | -1458x | +|||
382 | +! |
- logger::log_trace("CodeClass$append CodeClass appended.")+ h4("TealDataset Connector for: ", code(x$get_dataname())),+ |
+ ||
383 | +! | +
+ p(icon("check"), "Ready to Load") |
||
82 | +384 |
- }+ ) |
||
83 | +385 |
-
+ } |
||
84 | -3022x | +|||
386 | +! |
- return(invisible(self))+ ui |
||
85 | +387 |
- },+ } else { |
||
86 | -+ | |||
388 | +6x |
- #' @description+ div(h4("Data(set) for: ", lapply(x$get_datanames(), code)), p(icon("check"), "Loaded")) |
||
87 | +389 |
- #' Set code in form of character+ }, |
||
88 | -+ | |||
390 | +6x |
- #' @param code (`character`) vector of code text to be set+ br() |
||
89 | +391 |
- #' @param dataname optional, (`character`) vector of `datanames` to assign code to. If empty then the code+ ) |
||
90 | +392 |
- #' is considered to be "global"+ } |
||
91 | +393 |
- #' @param deps optional, (`character`) vector of `datanames` that given code depends on+ ),+ |
+ ||
394 | +4x | +
+ actionButton(inputId = ns("submit"), label = "Submit all") |
||
92 | +395 |
- #'+ ),+ |
+ ||
396 | +4x | +
+ `data-proxy-click` = ns("submit") # handled by jscode in custom.js - hit enter to submit |
||
93 | +397 |
- #' @return changed `CodeClass` object+ ) |
||
94 | +398 |
- set_code = function(code, dataname = character(0), deps = character(0)) {+ ) |
||
95 | -793x | +|||
399 | +
- checkmate::assert_character(code, min.len = 1, any.missing = FALSE)+ ) |
|||
96 | -793x | +|||
400 | +
- checkmate::assert_character(dataname, any.missing = FALSE)+ ) |
|||
97 | -793x | +|||
401 | +
- stopifnot(!(dataname %in% deps))+ }, |
|||
98 | +402 |
-
+ server = function(input, output, session) { |
||
99 | -793x | +|||
403 | +! |
- code <- pretty_code_string(code)+ logger::log_trace("TealData$server initializing...") |
||
100 | +404 | |||
101 | -793x | -
- for (code_single in code) {- |
- ||
102 | -822x | +|||
405 | +! |
- private$set_code_single(code_single, dataname, deps)+ shinyjs::show("delayed_data") |
||
103 | -+ | |||
406 | +! |
- }+ for (dc in self$get_connectors()) { |
||
104 | -793x | +|||
407 | +! |
- logger::log_trace("CodeClass$set_code code set.")+ if (inherits(dc, "TealDataConnector")) { |
||
105 | -793x | +|||
408 | +! |
- return(invisible(self))+ dc$get_preopen_server()(id = dc$id) |
||
106 | +409 |
- },+ } |
||
107 | +410 |
- #' @description+ } |
||
108 | -+ | |||
411 | +! |
- #' Get the code for a given data names+ rv <- reactiveVal(NULL) |
||
109 | -+ | |||
412 | +! |
- #' @param dataname optional, (`character`) vector of `datanames` for which the code is extracted.+ observeEvent(input$submit, { |
||
110 | -+ | |||
413 | +! |
- #' If `NULL` then get the code for all data names+ logger::log_trace("TealData$server@1 submit button clicked.") |
||
111 | +414 |
- #' @param deparse optional, (`logical`) whether to return the deparsed form of a call+ # load data from all connectors |
||
112 | -+ | |||
415 | +! |
- #' @return `character` or `list` of calls+ for (dc in self$get_connectors()) { |
||
113 | -+ | |||
416 | +! |
- get_code = function(dataname = NULL, deparse = TRUE) {+ if (inherits(dc, "TealDataConnector")) { |
||
114 | -273x | +|||
417 | +! |
- checkmate::assert_character(dataname, min.len = 1, null.ok = TRUE, any.missing = FALSE)+ dc$get_server()( |
||
115 | -273x | +|||
418 | +! |
- checkmate::assert_flag(deparse)+ id = dc$id, |
||
116 | -273x | +|||
419 | +! |
- if (is.null(dataname)) {+ connection = dc$get_connection(), |
||
117 | -234x | +|||
420 | +! |
- private$get_code_all(deparse = deparse)+ connectors = dc$get_items() |
||
118 | +421 |
- } else {+ ) |
||
119 | -39x | +|||
422 | +! |
- private$get_code_dataname(dataname = dataname, deparse = deparse)+ } else if (inherits(dc, "TealDatasetConnector")) { |
||
120 | -+ | |||
423 | +! |
- }+ dc$get_server()(id = dc$get_dataname()) |
||
121 | +424 |
- },+ } |
||
122 | -+ | |||
425 | +! |
- #' @description+ if (dc$is_failed()) { |
||
123 | -+ | |||
426 | +! |
- #' Evaluates internal code within given environment+ break |
||
124 | +427 |
- #' @param envir (`environment`) environment in which code will be evaluated+ } |
||
125 | +428 |
- #' @return invisibly `NULL`+ } |
||
126 | +429 |
- eval = function(envir = new.env(parent = parent.env(.GlobalEnv))) {- |
- ||
127 | -88x | -
- for (x in self$get_code(deparse = FALSE)) {+ |
||
128 | -121x | +|||
430 | +! |
- out <- tryCatch(+ if (self$is_pulled()) { |
||
129 | -121x | +|||
431 | +! |
- base::eval(x, envir = envir),+ logger::log_trace("TealData$server@1 data is pulled.") |
||
130 | -121x | +|||
432 | +! |
- error = function(e) e+ withProgress(value = 1, message = "Checking data reproducibility", { |
||
131 | +433 |
- )+ # We check first and then mutate. |
||
132 | +434 | - - | -||
133 | -121x | -
- if (inherits(out, "error")) {+ # mutate_code is reproducible by default we assume that we don't |
||
134 | -4x | +|||
435 | +
- error_msg <- sprintf(+ # have to check the result of the re-evaluation of the code |
|||
135 | -4x | +|||
436 | +! |
- "%s\n\nEvaluation of the code failed:\n %s", deparse1(x, collapse = "\n"), conditionMessage(out)+ self$check_reproducibility() |
||
136 | +437 |
- )+ }) |
||
137 | +438 | |||
138 | -4x | -
- rlang::with_options(- |
- ||
139 | -4x | +|||
439 | +! |
- stop(error_msg, call. = FALSE),+ withProgress(value = 1, message = "Executing processing code", { |
||
140 | -4x | +|||
440 | +! |
- warning.length = max(min(8170, nchar(error_msg) + 30), 100)+ self$execute_mutate() |
||
141 | -+ | |||
441 | +! |
- )+ self$check_metadata() |
||
142 | +442 |
- }+ }) |
||
143 | -+ | |||
443 | +! |
- }+ logger::log_info("Data ready to pass to the application.") |
||
144 | -84x | +|||
444 | +! |
- logger::log_trace("CodeClass$eval successfuly evaluated the code.")+ shinyjs::hide("delayed_data") |
||
145 | -84x | +|||
445 | +! |
- return(invisible(NULL))+ rv(self) |
||
146 | +446 |
- }+ } |
||
147 | +447 |
- ),+ }) |
||
148 | -+ | |||
448 | +! |
- private = list(+ return(rv) |
||
149 | +449 |
- ## __Private Fields ====+ } |
||
150 | +450 |
- .code = list(),+ ) |
||
151 | +451 |
- deps = list(),+ ) |
152 | +1 |
- ## __Private Methods ====+ #' S3 generic for creating an information summary about the duplicate key values in a dataset |
||
153 | +2 |
- set_code_single = function(code,+ #' |
||
154 | +3 |
- dataname = attr(code, "dataname"),+ #' @description `r lifecycle::badge("stable")` |
||
155 | +4 |
- deps = attr(code, "deps"),+ #' |
||
156 | +5 |
- id = attr(code, "id")) {- |
- ||
157 | -! | -
- if (is.null(dataname)) dataname <- character(0)- |
- ||
158 | -418x | -
- if (is.null(deps)) deps <- character(0)+ #' @details The information summary provides row numbers and number of duplicates |
||
159 | -822x | +|||
6 | +
- if (is.null(id)) id <- digest::digest(c(private$.code, code))+ #' for each duplicated key value. |
|||
160 | +7 |
- # Line shouldn't be added when it contains the same code and the same `dataname`+ #' |
||
161 | +8 |
- # as a line already present in an object of `CodeClass`+ #' @param dataset `TealDataset` or `data.frame` a dataset, which will be tested |
||
162 | +9 |
- if (+ #' @param keys `character` vector of variable names in `dataset` consisting the key |
||
163 | -3604x | +|||
10 | +
- !id %in% unlist(lapply(private$.code, "attr", "id")) ||+ #' or `keys` object, which does have a `primary` element with a vector of variable |
|||
164 | -3604x | +|||
11 | +
- all(+ #' names in `dataset` consisting the key. Optional, default: NULL |
|||
165 | -3604x | +|||
12 | +
- vapply(dataname, FUN.VALUE = logical(1), FUN = function(x) {+ #' |
|||
166 | -206x | +|||
13 | +
- !x %in% unlist(lapply(private$.code, "attr", "dataname"))+ #' @return a `tibble` with variables consisting the key and `row_no` and `duplicates_count` columns |
|||
167 | +14 |
- })+ #' |
||
168 | +15 |
- )+ #' @note Raises an exception when this function cannot determine the primary key columns of the tested object. |
||
169 | +16 |
- ) {+ #' |
||
170 | -3399x | +|||
17 | +
- attr(code, "dataname") <- dataname+ #' @examples |
|||
171 | -3399x | +|||
18 | +
- attr(code, "deps") <- deps+ #' |
|||
172 | -3399x | +|||
19 | +
- attr(code, "id") <- id+ #' adsl <- teal.data::example_cdisc_data("ADSL") |
|||
173 | +20 |
-
+ #' # create a TealDataset with default keys |
||
174 | -3399x | +|||
21 | +
- private$.code <- base::append(private$.code, list(code))+ #' rel_adsl <- cdisc_dataset("ADSL", adsl) |
|||
175 | +22 |
- }+ #' get_key_duplicates(rel_adsl) |
||
176 | -3604x | +|||
23 | +
- return(invisible(NULL))+ #' |
|||
177 | +24 |
- },+ #' df <- as.data.frame( |
||
178 | +25 |
- get_code_all = function(deparse) {+ #' list(a = c("a", "a", "b", "b", "c"), b = c(1, 2, 3, 3, 4), c = c(1, 2, 3, 4, 5)) |
||
179 | -234x | +|||
26 | +
- private$get_code_idx(idx = seq_along(private$.code), deparse = deparse)+ #' ) |
|||
180 | +27 |
- },+ #' res <- get_key_duplicates(df, keys = c("a", "b")) # duplicated keys are in rows 3 and 4 |
||
181 | +28 |
- get_code_dataname = function(dataname, deparse) {+ #' print(res) # prints a tibble |
||
182 | +29 |
- # the lines of code we need for the dataname+ #' \dontrun{ |
||
183 | -39x | +|||
30 | +
- res <- integer(0)+ #' get_key_duplicates(df) # raises an exception, because keys are missing with no default |
|||
184 | +31 |
- # the set of datanames we want code for code for initially just dataname+ #' } |
||
185 | -39x | +|||
32 | +
- datanames <- dataname+ #' |
|||
186 | +33 |
-
+ #' @export |
||
187 | +34 |
- # loop backwards along code+ get_key_duplicates <- function(dataset, keys = NULL) { |
||
188 | -39x | +35 | +46x |
- for (idx in rev(seq_along(private$.code))) {+ UseMethod("get_key_duplicates", dataset) |
189 | -170x | +|||
36 | +
- code_entry <- private$.code[[idx]]+ } |
|||
190 | +37 | |||
191 | +38 |
- # line of code is one we want if it is not empty and+ #' @rdname get_key_duplicates |
||
192 | +39 |
- # has any dataname attribute in the vector datanames or dataname starts with * or is global code and+ #' @export |
||
193 | +40 |
- # already have some lines of code selected+ get_key_duplicates.TealDataset <- function(dataset, keys = NULL) { # nolint |
||
194 | -+ | |||
41 | +! |
- if (+ df <- get_raw_data(dataset) |
||
195 | -+ | |||
42 | +! |
- (+ if (is.null(keys)) { |
||
196 | -170x | +|||
43 | +! |
- any(datanames %in% attr(code_entry, "dataname")) ||+ keys_ds <- get_keys(dataset) |
||
197 | -170x | +|||
44 | +! |
- any(grepl("^[*]", attr(code_entry, "dataname"))) ||+ keys <- if (is.null(keys_ds)) character(0) else keys_ds |
||
198 | -170x | +|||
45 | +
- (length(res) > 0 && length(attr(code_entry, "dataname")) == 0)+ } |
|||
199 | +46 |
- ) &&+ |
||
200 | -170x | +|||
47 | +! |
- length(code_entry) > 0+ get_key_duplicates_util(df, keys) |
||
201 | +48 |
- ) {+ } |
||
202 | +49 |
- # append to index of code we want+ |
||
203 | -92x | +|||
50 | +
- res <- c(idx, res)+ #' @rdname get_key_duplicates |
|||
204 | +51 |
-
+ #' @export |
||
205 | +52 |
- # and update datasets we want for preceding code with additional datanames and deps+ get_key_duplicates.data.frame <- function(dataset, keys = NULL) { # nolint |
||
206 | -92x | +53 | +46x |
- datanames <- unique(c(datanames, attr(code_entry, "dataname"), attr(code_entry, "deps")))+ if (is.null(keys)) { |
207 | -+ | |||
54 | +! |
- }+ attr_key <- attr(dataset, "primary_key")+ |
+ ||
55 | +! | +
+ keys <- if (is.null(attr_key)) character(0) else attr |
||
208 | +56 |
- }+ } |
||
209 | -39x | +57 | +46x |
- private$get_code_idx(idx = res, deparse = deparse)+ get_key_duplicates_util(dataset, keys) |
210 | +58 |
- },+ } |
||
211 | +59 |
- get_code_idx = function(idx, deparse) {+ |
||
212 | -273x | +|||
60 | +
- if (isFALSE(deparse)) {+ #' Creates a duplicate keys information summary. |
|||
213 | -107x | +|||
61 | +
- return(Filter(+ #' |
|||
214 | -107x | +|||
62 | +
- Negate(is.null),+ #' @details |
|||
215 | -107x | +|||
63 | +
- unname(unlist(lapply(+ #' Accepts a list of variable names - `keys`, which are treated as the |
|||
216 | -107x | +|||
64 | +
- private$.code[idx],+ #' key to the `data.frame` argument. An instance of duplicated key is |
|||
217 | -107x | +|||
65 | +
- function(x) sapply(x, function(i) text_to_call(i), simplify = FALSE)+ #' defined as two rows, which have the same values in columns defined by `keys`. |
|||
218 | +66 |
- )))+ #' Per each key value with duplicates returns a row in a `tibble`. The return table |
||
219 | +67 |
- ))+ #' has columns corresponding to the variable names passed in `keys` and |
||
220 | +68 |
- } else {+ #' two additional columns: `rows` and `n`, which provide |
||
221 | -166x | +|||
69 | +
- return(paste0(unlist(private$.code[idx]), collapse = "\n"))+ #' information about row numbers of the original dataframe, which contain duplicated keys |
|||
222 | +70 |
- }+ #' and total duplicates counts. |
||
223 | +71 |
- }+ #' |
||
224 | +72 |
- ),+ #' @param dataframe dataframe |
||
225 | +73 |
-
+ #' @param keys `character` vector of variable names consisting the key to the `data.frame` |
||
226 | +74 |
- ## __Active Fields ====+ #' |
||
227 | +75 |
- active = list(+ #' @return `data.frame` with a duplicate keys information summary |
||
228 | +76 |
- #' @field code (`list`) Derive the code of the dataset.+ #' |
||
229 | +77 |
- code = function() {+ #' @keywords internal |
||
230 | -4541x | +|||
78 | +
- private$.code+ #' |
|||
231 | +79 |
- }+ #' @examples |
||
232 | +80 |
- )+ #' df <- data.frame( |
||
233 | +81 |
- )+ #' a = c("a", "a", "b", "b", "c"), |
||
234 | +82 |
-
+ #' b = c(1, 2, 3, 3, 4), |
||
235 | +83 |
-
+ #' c = c(1, 2, 3, 4, 5) |
||
236 | +84 |
- ## Functions ====+ #' ) |
||
237 | +85 |
-
+ #' res <- teal.data:::get_key_duplicates_util(df, keys = c("a", "b")) |
||
238 | +86 |
- # Convert named list to `CodeClass` utilizing both `TealDatasetConnector` and `TealDataset`+ #' print(res) # duplicated keys are in rows 3 and 4 |
||
239 | +87 |
- list_to_code_class <- function(x) {+ #' @seealso [get_key_duplicates]+ |
+ ||
88 | ++ |
+ get_key_duplicates_util <- function(dataframe, keys) { |
||
240 | -1112x | +89 | +53x |
- checkmate::assert_list(x, min.len = 0, names = "unique")+ checkmate::assert_data_frame(dataframe)+ |
+
90 | +52x | +
+ checkmate::assert_character(keys)+ |
+ ||
91 | +50x | +
+ stopifnot(+ |
+ ||
92 | +50x | +
+ all(+ |
+ ||
93 | +50x | +
+ vapply(keys, FUN.VALUE = logical(1), FUN = function(key) key %in% colnames(dataframe)) |
||
241 | +94 |
-
+ ) |
||
242 | -1112x | +|||
95 | +
- res <- CodeClass$new()+ ) |
|||
243 | +96 | |||
97 | ++ |
+ # The goal is to print values of duplicated primary keys with number of duplicates and row numbers+ |
+ ||
244 | -1112x | +98 | +49x |
- if (length(x) > 0) {+ duplicates <- dataframe[, keys, drop = FALSE] |
245 | -163x | +99 | +49x |
- for (var_idx in seq_along(x)) {+ duplicates$dups <- duplicated(duplicates, fromLast = FALSE) | duplicated(duplicates, fromLast = TRUE) |
246 | -179x | +100 | +49x |
- var_value <- x[[var_idx]]+ duplicates$row_number <- seq_len(nrow(duplicates)) |
247 | -179x | +101 | +49x |
- var_name <- names(x)[[var_idx]]+ duplicates <- duplicates[duplicates$dups, ] |
248 | -179x | +102 | +49x |
- if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) {+ duplicates$dups <- NULL+ |
+
103 | ++ | + | ||
249 | -172x | +104 | +49x |
- res$append(var_value$get_code_class())+ if (nrow(duplicates) == 0) { |
250 | -172x | +105 | +45x |
- if (var_name != var_value$get_dataname()) {+ duplicates$rows <- character(0) |
251 | -136x | +106 | +45x |
- res$set_code(+ duplicates$row_number <- NULL |
252 | -136x | +107 | +45x |
- deparse1(call("<-", as.name(var_name), as.name(var_value$get_dataname())), collapse = "\n"),+ duplicates$n <- integer(0) |
253 | -136x | +108 | +45x |
- dataname = var_value$get_dataname()+ return(duplicates) |
254 | +109 |
- )+ } |
||
255 | +110 |
- }+ |
||
256 | -+ | |||
111 | +4x |
- } else {+ groups <- split(duplicates, duplicates[, keys, drop = FALSE], drop = TRUE) |
||
257 | -7x | +112 | +4x |
- var_code <- deparse1(call("<-", as.name(var_name), var_value), collapse = "\n")+ summary_list <- lapply(groups, function(group) { |
258 | -7x | +113 | +6x |
- res$set_code(var_code, var_name)+ ans <- group[1, keys, drop = FALSE] |
259 | -+ | |||
114 | +6x |
- }+ ans$rows <- paste(group[, "row_number"], collapse = ",") |
||
260 | -+ | |||
115 | +6x |
- }+ ans$n <- nrow(group)+ |
+ ||
116 | +6x | +
+ ans |
||
261 | +117 |
- }+ }) |
||
262 | -1112x | +118 | +4x |
- return(res)+ summary <- do.call(rbind, summary_list) |
263 | -+ | |||
119 | +4x |
- }+ rownames(summary) <- NULL+ |
+ ||
120 | +4x | +
+ summary |
||
264 | +121 |
-
+ } |
265 | +1 |
- #' Create call from string+ # CDISCTealDataConnector ------ |
||
266 | +2 |
#' |
||
267 | +3 |
- #' @param x (`character`) string containing the code.+ #' @title Manage multiple and `TealDatasetConnector` of the same type. |
||
268 | +4 |
#' |
||
269 | +5 |
- #' @return (`call`) object.+ #' @description `r lifecycle::badge("stable")` |
||
270 | +6 |
- #' @keywords internal+ #' Class manages `TealDatasetConnector` to specify additional dynamic arguments and to |
||
271 | +7 |
- text_to_call <- function(x) {- |
- ||
272 | -169x | -
- parsed <- parse(text = x, keep.source = FALSE)- |
- ||
273 | -169x | -
- if (length(parsed) == 0) {- |
- ||
274 | -4x | -
- return(NULL)+ #' open/close connection. |
||
275 | +8 |
- } else {- |
- ||
276 | -165x | -
- return(as.list(as.call(parsed))[[1]])+ #' |
||
277 | +9 |
- }+ #' @param connection (`TealDataConnection`)\cr |
||
278 | +10 |
- }+ #' connection to data source |
||
279 | +11 |
-
+ #' @param connectors (`list` of `TealDatasetConnector` elements)\cr |
||
280 | +12 |
- #' Format a vector of code into a string+ #' list with dataset connectors |
||
281 | +13 |
#' |
||
282 | +14 |
- #' @param code_vector (`character`) vector containing lines of+ CDISCTealDataConnector <- R6::R6Class( # nolint |
||
283 | +15 |
- #' code to format into a string.+ classname = "CDISCTealDataConnector", |
||
284 | +16 |
- #'+ inherit = TealDataConnector, |
||
285 | +17 |
- #' @return (`character`) string containing the formatted code.+ |
||
286 | +18 |
- #' @keywords internal+ ## __Public Methods ==== |
||
287 | +19 |
- pretty_code_string <- function(code_vector) {+ public = list( |
||
288 | +20 |
- # in order to remove bad formatting: text -> code -> text- |
- ||
289 | -812x | -
- unlist(lapply(- |
- ||
290 | -812x | -
- code_vector,- |
- ||
291 | -812x | -
- function(code_single) {+ #' @description |
||
292 | -814x | +|||
21 | +
- if (length(parse(text = code_single, keep.source = FALSE)) == 0) {+ #' Create a new `CDISCTealDataConnector` object |
|||
293 | +22 |
- # if string code cannot be passed into expression (e.g. code comment) then pass on the string+ initialize = function(connection, connectors) { |
||
294 | -11x | +23 | +8x |
- code_single+ super$initialize(connection = connection, connectors = connectors) |
295 | +24 |
- } else {+ |
||
296 | -803x | +25 | +8x |
- vapply(+ new_parent <- list() |
297 | -803x | +26 | +8x |
- as.list(as.call(parse(text = code_single, keep.source = FALSE))),+ for (x in connectors) { |
298 | -803x | +27 | +12x |
- deparse1,+ x_dataname <- x$get_dataname() |
299 | -803x | +28 | +12x |
- character(1),+ new_parent[[x_dataname]] <- if (inherits(x, "CDISCTealDatasetConnector")) { |
300 | -803x | +29 | +12x |
- collapse = "\n"+ x$get_parent() |
301 | +30 |
- )+ } else { |
||
302 | -+ | |||
31 | +! |
- }+ character(0L) |
||
303 | +32 |
- }+ } |
||
304 | +33 |
- ))+ } |
||
305 | +34 |
- }+ |
1 | -+ | |||
35 | +8x |
- #' Get dataset label attribute+ if (is_dag(new_parent)) { |
||
2 | -+ | |||
36 | +! |
- #'+ stop("Cycle detected in a parent and child dataset graph.") |
||
3 | +37 |
- #' @description `r lifecycle::badge("stable")`+ } |
||
4 | +38 |
- #'+ |
||
5 | -+ | |||
39 | +8x |
- #' @param data \code{data.frame} from which attribute is extracted+ private$parent <- new_parent |
||
6 | -+ | |||
40 | +8x |
- #'+ logger::log_trace( |
||
7 | -+ | |||
41 | +8x |
- #' @return (\code{character}) label or \code{NULL} if it is missing+ "CDISCTealDataConnector initialized with data: { paste(self$get_datanames(), collapse = ' ') }" |
||
8 | +42 |
- #'+ )+ |
+ ||
43 | +8x | +
+ return(invisible(self)) |
||
9 | +44 |
- #' @export+ }, |
||
10 | +45 |
- #'+ #' @description |
||
11 | +46 |
- #' @examples+ #' Get all datasets parent names |
||
12 | +47 |
- #' data_label(example_cdisc_data("ADSL"))+ #' @return (named `list`) with dataset name and its corresponding parent dataset name |
||
13 | +48 |
- data_label <- function(data) {+ get_parent = function() { |
||
14 | -179x | +|||
49 | +! |
- attr(data, "label")+ private$parent |
||
15 | +50 |
- }+ } |
||
16 | +51 |
-
+ ), |
||
17 | +52 |
- #' Set dataset label attribute+ |
||
18 | +53 |
- #'+ ## __Private Fields ==== |
||
19 | +54 |
- #' @description `r lifecycle::badge("stable")`+ private = list( |
||
20 | +55 |
- #'+ parent = list() # list with dataset names and its parent dataset names |
||
21 | +56 |
- #' @param x \code{data.frame} for which attribute is set+ ) |
||
22 | +57 |
- #' @param value (\code{character}) label+ ) |
||
23 | +58 |
- #'+ |
||
24 | +59 |
- #' @return modified \code{x} object+ #' The constructor of `CDISCTealDataConnector` objects. |
||
25 | +60 |
#' |
||
26 | +61 |
- #' @export+ #' @description `r lifecycle::badge("stable")` |
||
27 | +62 |
#' |
||
28 | +63 |
- #' @examples+ #' @param connection (`TealDataConnection`)\cr |
||
29 | +64 |
- #' x <- teal.data::example_cdisc_data("ADSL")+ #' connection to data source |
||
30 | +65 |
- #' data_label(x) <- "My custom label"+ #' @param connectors (`list` of `TealDatasetConnector` elements)\cr |
||
31 | +66 |
- #' data_label(x)+ #' list with dataset connectors |
||
32 | +67 |
- `data_label<-` <- function(x, value) { # nolint+ #' |
||
33 | -! | +|||
68 | +
- stopifnot(is.data.frame(x))+ #' @examples |
|||
34 | -! | +|||
69 | +
- checkmate::assert_string(value)+ #' adsl_cf <- callable_function( |
|||
35 | +70 |
-
+ #' function() as.data.frame(as.list(setNames(nm = get_cdisc_keys("ADSL")))) |
||
36 | -! | +|||
71 | +
- attr(x, "label") <- value+ #' ) |
|||
37 | -! | +|||
72 | +
- x+ #' adae_cf <- callable_function( |
|||
38 | +73 |
- }+ #' function() as.data.frame(as.list(setNames(nm = get_cdisc_keys("ADAE")))) |
||
39 | +74 |
-
+ #' ) |
||
40 | +75 |
- #' Function that returns the default keys for a `CDISC` dataset by name+ #' adsl <- cdisc_dataset_connector( |
||
41 | +76 |
- #'+ #' "ADSL", adsl_cf, |
||
42 | +77 |
- #' @description `r lifecycle::badge("stable")`+ #' keys = get_cdisc_keys("ADSL"), parent = character(0) |
||
43 | +78 |
- #'+ #' ) |
||
44 | +79 |
- #' @param dataname name of the `CDISC` dataset+ #' adae <- cdisc_dataset_connector( |
||
45 | +80 |
- #'+ #' "ADAE", adae_cf, |
||
46 | +81 |
- #' @return \code{keys} object+ #' keys = get_cdisc_keys("ADAE"), parent = "ADSL" |
||
47 | +82 |
- #'+ #' ) |
||
48 | +83 |
- #' @export+ #' data <- cdisc_data_connector( |
||
49 | +84 |
- #'+ #' connection = data_connection(open_fun = callable_function(function() "open function")), |
||
50 | +85 |
- #' @examples+ #' connectors = list(adsl, adae) |
||
51 | +86 |
- #' get_cdisc_keys("ADSL")+ #' ) |
||
52 | +87 |
- get_cdisc_keys <- function(dataname) {+ #' @return `CDISCTealDataConnector` object |
||
53 | -371x | +|||
88 | +
- checkmate::assert_string(dataname)+ #' @export |
|||
54 | +89 |
-
+ cdisc_data_connector <- function(connection, connectors) { |
||
55 | -371x | -
- if (!(dataname %in% names(default_cdisc_keys))) {- |
- ||
56 | -! | -
- stop(paste(sprintf("get_cdisc_keys does not support datasets called %s", dataname),- |
- ||
57 | -! | -
- " Please specify the keys directly, for example:",- |
- ||
58 | -! | -
- sprintf(- |
- ||
59 | -! | -
- " cdisc_dataset(dataname = \"%s\", keys = c(\"STUDYID\", \"USUBJID\", ...), parent = \"ADSL\", ...)",- |
- ||
60 | -! | +90 | +9x |
- dataname+ stopifnot(inherits(connection, "TealDataConnection")) |
61 | -+ | |||
91 | +7x |
- ),+ checkmate::assert_list(connectors, types = "TealDatasetConnector", min.len = 1) |
||
62 | -! | +|||
92 | +5x |
- sep = "\n"+ CDISCTealDataConnector$new(connection, connectors) |
||
63 | +93 |
- ))+ } |
64 | +1 |
- } else {- |
- ||
65 | -371x | -
- cdisc_keys <- default_cdisc_keys[[dataname]]$primary+ ## TealDataAbstract ==== |
||
66 | +2 |
-
+ #' @title `TealDataAbstract` class |
||
67 | -371x | +|||
3 | +
- return(cdisc_keys)+ #' |
|||
68 | +4 |
- }+ #' @description |
||
69 | +5 |
- }+ #' Abstract class containing code for handling set of datasets. |
||
70 | +6 |
-
+ #' @keywords internal |
||
71 | +7 |
- #' Extracts dataset and variable labels from a dataset.+ TealDataAbstract <- R6::R6Class( # nolint |
||
72 | +8 |
- #'+ classname = "TealDataAbstract", |
||
73 | +9 |
- #' @description `r lifecycle::badge("stable")`+ ## __Public Methods ==== |
||
74 | +10 |
- #'+ public = list( |
||
75 | +11 |
- #' @param data (`data.frame`) table to extract the labels from+ #' @description |
||
76 | +12 |
- #' @param fill (`logical(1)`) if `TRUE`, the function will return variable names for columns with non-existent labels;+ #' Cannot create a `TealDataAbstract` object |
||
77 | +13 |
- #' otherwise will return `NA` for them+ #' |
||
78 | +14 |
- #'+ #' @return throws error |
||
79 | +15 |
- #' @return `list` with two keys: `dataset_labels` and `column_labels`+ initialize = function() { |
||
80 | -+ | |||
16 | +1x |
- #'+ stop("Pure virtual method") |
||
81 | +17 |
- #' @export+ }, |
||
82 | +18 |
- #'+ #' @description |
||
83 | +19 |
- #' @examples+ #' Check if the object raw data is reproducible from the `get_code()` code. |
||
84 | +20 |
- #' iris_with_labels <- iris+ #' @return |
||
85 | +21 |
- #' attr(iris_with_labels, "label") <- "Custom iris dataset with labels"+ #' `NULL` if check step has been disabled |
||
86 | +22 |
- #' attr(iris_with_labels["Sepal.Length"], "label") <- c(`Sepal.Length` = "Sepal Length")+ #' `TRUE` if all the datasets generated from evaluating the |
||
87 | +23 |
- #' get_labels(iris_with_labels, fill = TRUE)+ #' `get_code()` code are identical to the raw data, else `FALSE`. |
||
88 | +24 |
- #' get_labels(iris_with_labels, fill = FALSE)+ check = function() { |
||
89 | +25 |
- get_labels <- function(data, fill = TRUE) {+ # code can be put only to the mutate with empty code in datasets |
||
90 | -8x | +26 | +57x |
- stopifnot(is.data.frame(data))+ res <- if (isFALSE(private$.check)) { |
91 | -8x | +27 | +44x |
- checkmate::assert_flag(fill)+ NULL |
92 | +28 |
-
+ } else { |
||
93 | -8x | +29 | +13x |
- column_labels <- Map(function(col, colname) {+ if (length(private$pull_code$code) > 0) { |
94 | -27x | +30 | +1x |
- label <- attr(col, "label")+ private$check_combined_code() |
95 | -27x | +|||
31 | +
- if (is.null(label)) {+ } else { |
|||
96 | -25x | +32 | +12x |
- if (fill) {+ all(vapply( |
97 | -20x | +33 | +12x |
- colname+ private$datasets, |
98 | -+ | |||
34 | +12x |
- } else {+ function(x) { |
||
99 | -8x | +35 | +27x |
- NA_character_+ check_res <- x$check() |
100 | +36 |
- }+ # NULL is still ok |
||
101 | -+ | |||
37 | +26x |
- } else {+ is.null(check_res) || isTRUE(check_res) |
||
102 | -2x | +|||
38 | +
- if (!checkmate::test_string(label, na.ok = TRUE)) {+ }, |
|||
103 | -! | +|||
39 | +12x |
- stop("label for variable ", colname, " is not a character string")+ logical(1) |
||
104 | +40 |
- }+ )) |
||
105 | -2x | +|||
41 | +
- as.vector(label) # because label might be a named vector+ } |
|||
106 | +42 |
- }+ } |
||
107 | -8x | +43 | +56x |
- }, data, colnames(data))+ private$check_result <- res |
108 | -8x | -
- column_labels <- unlist(column_labels, recursive = FALSE, use.names = TRUE)- |
- ||
109 | -+ | 44 | +56x |
-
+ logger::log_trace("TealDataAbstract$check executed the code to reproduce the data - result: { res }.") |
110 | -8x | +45 | +56x |
- list("dataset_label" = data_label(data), "column_labels" = column_labels)+ res |
111 | +46 |
- }+ }, |
1 | +47 |
- ## Callable ====+ #' @description |
||
2 | +48 |
- #'+ #' Execute `check()` and raise an error if it's not reproducible. |
||
3 | +49 |
- #' @title A \code{Callable} class of objects+ #' @return error if code is not reproducible else invisibly nothing |
||
4 | +50 |
- #'+ check_reproducibility = function() { |
||
5 | -+ | |||
51 | +47x |
- #' @description Object that stores function name with its arguments. Methods to get call and run it.+ self$check() |
||
6 | -+ | |||
52 | +47x |
- #' @keywords internal+ if (isFALSE(self$get_check_result())) { |
||
7 | -+ | |||
53 | +2x |
- #'+ stop("Reproducibility check failed.") |
||
8 | +54 |
- Callable <- R6::R6Class( # nolint+ } |
||
9 | -+ | |||
55 | +45x |
- "Callable",+ logger::log_trace("TealDataAbstract$check_reproducibility reproducibility check passed.") |
||
10 | -+ | |||
56 | +45x |
-
+ return(invisible(NULL)) |
||
11 | +57 |
- ## __Public Methods ====+ }, |
||
12 | +58 |
- public = list(+ #' @description |
||
13 | +59 |
- #' @description+ #' Execute mutate code. Using `mutate_data(set).TealDataAbstract` |
||
14 | +60 |
- #' Create a new \code{CallableCode} object+ #' does not cause instant execution, the `mutate_code` is |
||
15 | +61 |
- #'+ #' delayed and can be evaluated using this method. |
||
16 | +62 |
- #' @param env (\code{environment})\cr+ execute_mutate = function() { |
||
17 | -+ | |||
63 | +2x |
- #' environment where the call will be evaluated+ logger::log_trace("TealDataAbstract$execute_mutate evaluating mutate code...") |
||
18 | +64 |
- #'+ # this will be pulled already! - not needed? |
||
19 | -+ | |||
65 | +2x |
- #' @return new \code{CallableCode} object+ if (length(private$mutate_code$code) == 0) { |
||
20 | -+ | |||
66 | +1x |
- initialize = function(env) {+ res <- unlist(lapply( |
||
21 | -230x | +67 | +1x |
- stopifnot(is.environment(env))+ private$datasets, |
22 | -230x | +68 | +1x |
- private$env <- env+ function(x) { |
23 | -230x | +69 | +2x |
- logger::log_trace("Callable initialized.")+ if (is_pulled(x)) { |
24 | -230x | +70 | +2x |
- invisible(self)+ get_datasets(x) |
25 | +71 |
- },+ } else { |
||
26 | -+ | |||
72 | +! |
- #' @description+ NULL |
||
27 | +73 |
- #' Assigns \code{x <- value} object to \code{env}. Assigned object can't+ } |
||
28 | +74 |
- #' be modified within local environment as it will be locked by using+ } |
||
29 | +75 |
- #' \code{lockBinding}. This also means that this object can't be reassigned+ )) |
||
30 | +76 |
- #' which will throw an error.+ # exit early if mutate isn't required |
||
31 | -+ | |||
77 | +1x |
- #' @param x (\code{character} value)\cr+ logger::log_trace("TealDataAbstract$execute_mutate no code to evaluate.")+ |
+ ||
78 | +1x | +
+ if (!is.null(res)) {+ |
+ ||
79 | +1x | +
+ res <- stats::setNames(res, vapply(res, get_dataname, character(1))) |
||
32 | +80 |
- #' name of the variable in class environment+ }+ |
+ ||
81 | +1x | +
+ return(res) |
||
33 | +82 |
- #' @param value (\code{data.frame})\cr+ } |
||
34 | +83 |
- #' object to be assigned to \code{x}+ + |
+ ||
84 | +1x | +
+ if (inherits(private$mutate_code, "PythonCodeClass")) {+ |
+ ||
85 | +! | +
+ items <- lapply(self$get_items(), get_raw_data)+ |
+ ||
86 | +! | +
+ datasets <- stats::setNames(items, vapply(self$get_items(), get_dataname, character(1))) |
||
35 | +87 |
- #'+ + |
+ ||
88 | +! | +
+ new_env <- private$mutate_code$eval(vars = c(datasets, private$mutate_vars)) |
||
36 | +89 |
- #' @return (\code{self}) invisibly for chaining.+ } else { |
||
37 | +90 |
- assign_to_env = function(x, value) {+ # have to evaluate post-processing code (i.e. private$mutate_code) before returning dataset |
||
38 | -+ | |||
91 | +1x |
- # assign variable once+ new_env <- new.env(parent = parent.env(globalenv())) |
||
39 | -63x | +92 | +1x |
- if (!exists(x, envir = private$env)) {+ for (dataset in self$get_items()) { |
40 | -54x | +93 | +2x |
- assign(x, value, envir = private$env)+ assign(get_dataname(dataset), get_raw_data(dataset), envir = new_env) |
41 | +94 |
-
+ } |
||
42 | +95 |
- # variable can't be modified+ |
||
43 | -54x | +96 | +1x |
- lockBinding(sym = x, env = private$env)+ for (var_idx in seq_along(private$mutate_vars)) { |
44 | -54x | +|||
97 | +! |
- logger::log_trace("Callable$assign_to_env assigned '{ x }' to the environment.")+ mutate_var <- private$mutate_vars[[var_idx]] |
||
45 | -+ | |||
98 | +! |
- }+ assign( |
||
46 | -+ | |||
99 | +! |
-
+ x = names(private$mutate_vars)[[var_idx]], |
||
47 | -63x | +|||
100 | +! |
- return(invisible(self))+ value = `if`( |
||
48 | -+ | |||
101 | +! |
- },+ inherits(mutate_var, "TealDataset") || inherits(mutate_var, "TealDatasetConnector"), |
||
49 | -+ | |||
102 | +! |
- #' @description+ get_raw_data(mutate_var), |
||
50 | -+ | |||
103 | +! |
- #' Execute \code{Callable} function or code.+ mutate_var |
||
51 | +104 |
- #'+ ), |
||
52 | -+ | |||
105 | +! |
- #' @param return (\code{logical} value)\cr+ envir = new_env |
||
53 | +106 |
- #' whether to return an object+ ) |
||
54 | +107 |
- #' @param args (\code{NULL} or named \code{list})\cr+ } |
||
55 | +108 |
- #' supplied for callable functions only, these are dynamic arguments passed to function.+ |
||
56 | -+ | |||
109 | +1x |
- #' Dynamic arguments are executed in this call and are not saved which means that+ private$mutate_code$eval(envir = new_env) |
||
57 | +110 |
- #' \code{self$get_call()} won't include them later.+ } |
||
58 | +111 |
- #' @param try (\code{logical} value)\cr+ |
||
59 | -+ | |||
112 | +1x |
- #' whether perform function evaluation inside \code{try} clause+ lapply( |
||
60 | -+ | |||
113 | +1x |
- #'+ self$get_datasets(), |
||
61 | -+ | |||
114 | +1x |
- #' @return nothing or output from function depending on \code{return}+ function(x) { |
||
62 | -+ | |||
115 | +2x |
- #' argument. If \code{run} fails it will return object of class \code{simple-error error}+ x$recreate(+ |
+ ||
116 | +2x | +
+ x = get(get_dataname(x), new_env) |
||
63 | +117 |
- #' when \code{try = TRUE} or will stop if \code{try = FALSE}.+ ) |
||
64 | +118 |
- run = function(return = TRUE, args = NULL, try = FALSE) {+ } |
||
65 | -150x | +|||
119 | +
- checkmate::assert_flag(return)+ ) |
|||
66 | -150x | +120 | +1x |
- checkmate::assert_list(args, names = "unique", min.len = 0, null.ok = TRUE)+ logger::log_trace("TealDataAbstract$execute_mutate evaluated mutate code.") |
67 | -150x | +121 | +1x |
- checkmate::assert_flag(try)+ return(invisible(NULL)) |
68 | +122 |
-
+ }, |
||
69 | +123 |
- # args are "dynamic" are used only to evaluate this call+ #' @description |
||
70 | +124 |
- # - args not saved to private$call persistently+ #' Get result of reproducibility check |
||
71 | -150x | +|||
125 | +
- expr <- self$get_call(deparse = FALSE, args = args)+ #' @return `NULL` if check has not been called yet, `TRUE` / `FALSE` otherwise |
|||
72 | +126 |
-
+ get_check_result = function() { |
||
73 | -150x | +127 | +49x |
- res <- tryCatch(+ private$check_result |
74 | -150x | +|||
128 | +
- eval(expr, envir = private$env),+ }, |
|||
75 | -150x | +|||
129 | +
- error = function(e) e+ #' @description |
|||
76 | +130 |
- )+ #' Get code for all datasets. |
||
77 | -150x | +|||
131 | +
- private$check_run_output(res, try = try)+ #' @param dataname (`character`) `dataname` or `NULL` for all datasets |
|||
78 | +132 |
-
+ #' @param deparse (`logical`) whether to return the deparsed form of a call |
||
79 | -145x | +|||
133 | +
- logger::log_trace("Callable$run callable has been run.")+ #' @return (`character`) vector of code to generate datasets.+ |
+ |||
134 | ++ |
+ get_code = function(dataname = NULL, deparse = TRUE) { |
||
80 | -145x | +135 | +47x |
- if (return) {+ checkmate::assert_character(dataname, min.len = 1, null.ok = TRUE, any.missing = FALSE) |
81 | -144x | +136 | +46x |
- return(res)+ checkmate::assert_flag(deparse) |
82 | +137 |
- } else {+ |
||
83 | -1x | +138 | +45x |
- return(invisible(NULL))+ return(self$get_code_class()$get_code(dataname = dataname, deparse = deparse)) |
84 | +139 |
- }+ }, |
||
85 | +140 |
- },+ #' @description |
||
86 | +141 |
- #' @description+ #' Get internal `CodeClass` object |
||
87 | +142 |
- #' Check if evaluation of the function has not failed.+ #' @param only_pull (`logical` value)\cr |
||
88 | +143 |
- #'+ #' if `TRUE` only code to pull datasets will be returned without the mutate code. |
||
89 | +144 |
- #' @return (\code{logical}) \code{TRUE} if evaluation of the function failed or \code{FALSE}+ #' |
||
90 | +145 |
- #' if evaluation failed or function hasn't yet been called.+ #' @return `CodeClass` |
||
91 | +146 |
- is_failed = function() {+ get_code_class = function(only_pull = FALSE) { |
||
92 | -151x | +147 | +46x |
- return(private$failed)+ all_code_class <- CodeClass$new() |
93 | +148 |
- },+ |
||
94 | -+ | |||
149 | +46x |
- #' @description+ pull_code_class <- private$get_pull_code_class() |
||
95 | -+ | |||
150 | +46x |
- #' Get error message from last function execution+ all_code_class$append(pull_code_class) |
||
96 | +151 |
- #'+ |
||
97 | -+ | |||
152 | +46x |
- #' @return (\code{character}) object with error message or \code{character(0)} if last+ datasets_code_class <- private$get_datasets_code_class() |
||
98 | -+ | |||
153 | +46x |
- #' function evaluation was successful.+ all_code_class$append(datasets_code_class) |
||
99 | +154 |
- get_error_message = function() {+ |
||
100 | -3x | +155 | +46x |
- return(private$error_msg)+ if (isFALSE(only_pull)) { |
101 | -+ | |||
156 | +41x |
- }+ mutate_code_class <- private$get_mutate_code_class() |
||
102 | -+ | |||
157 | +41x |
- ),+ all_code_class$append(mutate_code_class) |
||
103 | +158 |
-
+ } |
||
104 | +159 |
- ## __Private Fields ====+ |
||
105 | -+ | |||
160 | +46x |
- private = list(+ return(all_code_class) |
||
106 | +161 |
- call = NULL, # a call object+ }, |
||
107 | +162 |
- env = NULL, # environment where function is called+ #' @description |
||
108 | +163 |
- failed = FALSE,+ #' Get names of the datasets. |
||
109 | +164 |
- error_msg = character(0),+ #' |
||
110 | +165 |
- ## __Private Methods ====+ #' @return `character` vector with names of all datasets. |
||
111 | +166 |
-
+ get_datanames = function() { |
||
112 | -+ | |||
167 | +209x |
- # The deep clone function deep clones the environment of the Callable so+ datasets_names <- unname(unlist(lapply(private$datasets, get_dataname))) |
||
113 | +168 |
- # that it is distinct for the copy+ |
||
114 | -+ | |||
169 | +209x |
- deep_clone = function(name, value) {+ return(datasets_names) |
||
115 | -155x | +|||
170 | +
- deep_clone_r6(name, value)+ }, |
|||
116 | +171 |
- },+ #' @description |
||
117 | +172 |
- # Checks output and handles error messages+ #' Get `TealDataset` object. |
||
118 | +173 |
- check_run_output = function(res, try) {+ #' |
||
119 | -150x | +|||
174 | +
- if (inherits(res, "error")) {+ #' @param dataname (`character` value)\cr |
|||
120 | -8x | +|||
175 | +
- msg <- conditionMessage(res)+ #' name of dataset to be returned. If `NULL`, all datasets are returned. |
|||
121 | -8x | +|||
176 | +
- is_locked <- grepl(pattern = "cannot change value of locked", x = msg)+ #' |
|||
122 | +177 |
-
+ #' @return `TealDataset`. |
||
123 | -8x | +|||
178 | +
- error_msg <- if (is_locked) {+ get_dataset = function(dataname = NULL) { |
|||
124 | -2x | +179 | +5x |
- locked_var <- gsub("^.+\\'(.+)\\'$", "\\1", x = msg)+ checkmate::assert_string(dataname, null.ok = TRUE) |
125 | -2x | +|||
180 | +
- sprintf(+ |
|||
126 | -2x | +181 | +4x |
- "Modification of the local variable '%1$s' is not allowed. %2$s '%1$s'",+ if (length(dataname) == 1) { |
127 | -2x | +182 | +3x |
- locked_var,+ if (!(dataname %in% self$get_datanames())) { |
128 | -2x | +183 | +1x |
- "Please add proxy variable to CallableCode to obtain results depending on altered"+ stop(paste("dataset", dataname, "not found")) |
129 | +184 |
- )+ } |
||
130 | +185 |
- } else {+ |
||
131 | -6x | +186 | +2x |
- msg+ res <- self$get_datasets()[[dataname]] |
132 | -+ | |||
187 | +2x |
- }+ return(res) |
||
133 | +188 |
-
+ } else { |
||
134 | -8x | +189 | +1x |
- if (try) {+ return(self$get_datasets()) |
135 | -3x | +|||
190 | +
- private$failed <- TRUE+ } |
|||
136 | -3x | +|||
191 | +
- private$error_msg <- error_msg+ }, |
|||
137 | -3x | +|||
192 | +
- logger::log_error("Callable$check_run_output { deparse1(error_msg) }.")+ #' @description |
|||
138 | +193 |
- } else {+ #' Get `list` of `TealDataset` objects. |
||
139 | -5x | +|||
194 | +
- stop(error_msg, call. = FALSE)+ #' |
|||
140 | +195 |
- }+ #' @return `list` of `TealDataset`. |
||
141 | +196 |
- } else {+ get_datasets = function() { |
||
142 | -142x | +197 | +62x |
- private$failed <- FALSE+ if (!self$is_pulled()) { |
143 | -142x | -
- private$error_msg <- character(0)- |
- ||
144 | -+ | 198 | +2x |
- }+ stop( |
145 | -+ | |||
199 | +2x |
- }+ "Not all datasets have been pulled yet.\n", |
||
146 | -+ | |||
200 | +2x |
- )+ "- Please use `load_datasets()` to retrieve complete results." |
||
147 | +201 |
- )+ ) |
1 | +202 |
- ## CDISCTealDataset ====+ } |
||
2 | -+ | |||
203 | +60x |
- #'+ unlist(lapply(self$get_items(), get_dataset)) |
||
3 | +204 |
- #' @title R6 Class representing a dataset with parent attribute+ }, |
||
4 | +205 |
- #'+ #' @description |
||
5 | +206 |
- #' @description `r lifecycle::badge("stable")`+ #' Get all datasets and all dataset connectors |
||
6 | +207 |
- #' Any `data.frame` object can be stored inside this object.+ #' |
||
7 | +208 |
- #'+ #' @param dataname (`character` value)\cr |
||
8 | +209 |
- #' The difference compared to `TealDataset` class is a parent field that+ #' name of dataset connector to be returned. If `NULL`, all connectors are returned. |
||
9 | +210 |
- #' indicates name of the parent dataset. Note that the parent field might+ #' @return `list` with all datasets and all connectors |
||
10 | +211 |
- #' be empty (i.e. `character(0)`).+ get_items = function(dataname = NULL) { |
||
11 | -+ | |||
212 | +34x |
- #'+ checkmate::assert_string(dataname, null.ok = TRUE) |
||
12 | +213 |
- #' @param dataname (`character`)\cr+ |
||
13 | -+ | |||
214 | +34x |
- #' A given name for the dataset it may not contain spaces+ if (length(dataname) == 1) { |
||
14 | -+ | |||
215 | +! |
- #'+ if (!(dataname %in% self$get_datanames())) { |
||
15 | -+ | |||
216 | +! |
- #' @param x (`data.frame`)\cr+ stop(paste("dataset", dataname, "not found")) |
||
16 | +217 |
- #'+ } |
||
17 | -+ | |||
218 | +! |
- #' @param keys (`character`)\cr+ return(private$datasets[[dataname]]) |
||
18 | +219 |
- #' vector with primary keys+ } else { |
||
19 | -+ | |||
220 | +34x |
- #'+ return(private$datasets) |
||
20 | +221 |
- #' @param parent optional, (`character`) \cr+ } |
||
21 | +222 |
- #' parent dataset name+ }, |
||
22 | +223 |
- #'+ #' @description |
||
23 | +224 |
- #' @param code (`character`)\cr+ #' Has this data been or will this data be subjected to a reproducibility check |
||
24 | +225 |
- #' A character string defining the code needed to produce the data set in `x`+ #' @return `logical` |
||
25 | +226 |
- #'+ get_check = function() { |
||
26 | -+ | |||
227 | +3x |
- #' @param label (`character`)\cr+ private$.check |
||
27 | +228 |
- #' Label to describe the dataset+ }, |
||
28 | +229 |
- #'+ #' @field id String used to create unique GUI elements |
||
29 | +230 |
- #' @param vars (named `list`)) \cr+ id = NULL, |
||
30 | +231 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ #' @description |
||
31 | +232 |
- #' other constant value, this/these object(s) should be included as named+ #' Check if dataset has already been pulled. |
||
32 | +233 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ #' |
||
33 | +234 |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ #' @return `TRUE` if dataset has been already pulled, else `FALSE` |
||
34 | +235 |
- #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to+ is_pulled = function() { |
||
35 | -+ | |||
236 | +128x |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ all(vapply(private$datasets, is_pulled, logical(1))) |
||
36 | +237 |
- #' are included to this object as local `vars` and they cannot be modified+ }, |
||
37 | +238 |
- #' within another dataset.+ #' @description |
||
38 | +239 |
- #'+ #' Mutate data by code. Code used in this mutation is not linked to particular |
||
39 | +240 |
- #' @param metadata (named `list` or `NULL`) \cr+ #' but refers to all datasets. |
||
40 | +241 |
- #' Field containing metadata about the dataset. Each element of the list+ #' Consequence of this is that when using `get_code(<dataset>)` this |
||
41 | +242 |
- #' should be atomic and length one.+ #' part of the code will be returned for each specified dataset. This method |
||
42 | +243 |
- #'+ #' should be used only if particular call involve changing multiple datasets. |
||
43 | +244 |
- #' @examples+ #' Otherwise please use `mutate_dataset`. |
||
44 | +245 |
- #' x <- cdisc_dataset(+ #' Execution of `mutate_code` is delayed after datasets are pulled |
||
45 | +246 |
- #' dataname = "XYZ",+ #' (`isTRUE(is_pulled)`). |
||
46 | +247 |
- #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE),+ #' |
||
47 | +248 |
- #' keys = "y",+ #' @param code (`character`) Code to mutate the dataset. Must contain the |
||
48 | +249 |
- #' parent = "ABC",+ #' `dataset$dataname` |
||
49 | +250 |
- #' code = "XYZ <- data.frame(x = c(1, 2), y = c('aa', 'bb'),+ #' @param vars (named `list`)) \cr |
||
50 | +251 |
- #' stringsAsFactors = FALSE)",+ #' In case when this object code depends on other `TealDataset` object(s) or |
||
51 | +252 |
- #' metadata = list(type = "example")+ #' other constant value, this/these object(s) should be included as named |
||
52 | +253 |
- #' )+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
53 | +254 |
- #'+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
||
54 | +255 |
- #' x$ncol+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
55 | +256 |
- #' x$get_code()+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
||
56 | +257 |
- #' x$get_dataname()+ #' are included to this object as local `vars` and they cannot be modified |
||
57 | +258 |
- #' x$get_keys()+ #' within another dataset. |
||
58 | +259 |
- #' x$get_parent()+ #' |
||
59 | +260 |
- CDISCTealDataset <- R6::R6Class( # nolint+ #' @return self invisibly for chaining |
||
60 | +261 |
- "CDISCTealDataset",+ mutate = function(code, vars = list()) { |
||
61 | -+ | |||
262 | +8x |
- inherit = TealDataset,+ private$set_mutate_vars(vars) |
||
62 | -+ | |||
263 | +8x |
- ## __Public Methods ====+ private$set_mutate_code( |
||
63 | -+ | |||
264 | +8x |
- public = list(+ code = code, |
||
64 | -+ | |||
265 | +8x |
- #' @description+ deps = names(vars) |
||
65 | +266 |
- #' Create a new object of `CDISCTealDataset` class+ ) |
||
66 | -+ | |||
267 | +8x |
- initialize = function(dataname, x, keys, parent, code = character(0),+ private$check_result <- NULL |
||
67 | -+ | |||
268 | +8x |
- label = character(0), vars = list(), metadata = NULL) {+ logger::log_trace( |
||
68 | -81x | +269 | +8x |
- checkmate::assert_character(parent, max.len = 1, any.missing = FALSE)+ sprintf( |
69 | -80x | +270 | +8x |
- super$initialize(+ "TealDataAbstract$mutate code (%s lines) and vars (%s) set.", |
70 | -80x | +271 | +8x |
- dataname = dataname, x = x, keys = keys, code = code,+ length(parse(text = code, keep.source = FALSE)), |
71 | -80x | +272 | +8x |
- label = label, vars = vars, metadata = metadata+ paste(names(vars), collapse = ", ") |
72 | +273 |
- )+ ) |
||
73 | +274 | - - | -||
74 | -80x | -
- self$set_parent(parent)- |
- ||
75 | -80x | -
- logger::log_trace("CDISCTealDataset initialized for dataset: { deparse1(self$get_dataname()) }.")+ ) |
||
76 | -80x | +275 | +8x |
return(invisible(self)) |
77 | +276 |
}, |
||
78 | +277 |
#' @description |
||
79 | +278 |
- #' Recreate a dataset with its current attributes+ #' Mutate dataset by code. |
||
80 | +279 |
- #' This is useful way to have access to class initialize method basing on class object+ #' Execution of `mutate_code` is delayed after datasets are pulled |
||
81 | +280 |
- #'+ #' (`isTRUE(is_pulled)`). |
||
82 | +281 |
- #' @return a new object of `CDISCTealDataset` class+ #' |
||
83 | +282 |
- recreate = function(dataname = self$get_dataname(),+ #' @param dataname (`character`) `Dataname` to be mutated |
||
84 | +283 |
- x = self$get_raw_data(),+ #' @param code (`character`) Code to mutate the dataset. Must contain the |
||
85 | +284 |
- keys = self$get_keys(),+ #' `dataset$dataname` |
||
86 | +285 |
- parent = self$get_parent(),+ #' @param vars (named `list`)) \cr |
||
87 | +286 |
- code = private$code,+ #' In case when this object code depends on other `TealDataset` object(s) or |
||
88 | +287 |
- label = self$get_dataset_label(),+ #' other constant value, this/these object(s) should be included as named |
||
89 | +288 |
- vars = list(),+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
90 | +289 |
- metadata = self$get_metadata()) {+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
||
91 | -8x | +|||
290 | +
- res <- self$initialize(+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
|||
92 | -8x | +|||
291 | +
- dataname = dataname,+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
|||
93 | -8x | +|||
292 | +
- x = x,+ #' are included to this object as local `vars` and they cannot be modified |
|||
94 | -8x | +|||
293 | +
- keys = keys,+ #' within another dataset. |
|||
95 | -8x | +|||
294 | +
- parent = parent,+ #' |
|||
96 | -8x | +|||
295 | +
- code = code,+ #' @return self invisibly for chaining |
|||
97 | -8x | +|||
296 | +
- label = label,+ mutate_dataset = function(dataname, code, vars = list()) { |
|||
98 | -8x | +297 | +7x |
- vars = vars,+ checkmate::assert_character(dataname, min.len = 1, any.missing = FALSE) |
99 | -8x | +298 | +6x |
- metadata = metadata+ stopifnot(all(dataname %in% self$get_datanames())) |
100 | +299 |
- )+ |
||
101 | -8x | +300 | +5x |
- logger::log_trace("CDISCTealDataset$recreate recreated dataset: { deparse1(self$get_dataname()) }.")+ private$set_mutate_vars(vars = vars) |
102 | -8x | +301 | +5x |
- return(res)+ private$set_mutate_code( |
103 | -+ | |||
302 | +5x |
- },+ code = code, |
||
104 | -+ | |||
303 | +5x |
- #' @description+ dataname = dataname, |
||
105 | -+ | |||
304 | +5x |
- #' Get all dataset attributes+ deps = names(vars) |
||
106 | +305 |
- #' @return (named `list`) with dataset attributes+ ) |
||
107 | +306 |
- get_attrs = function() {- |
- ||
108 | -! | -
- x <- super$get_attrs()+ |
||
109 | -! | +|||
307 | +5x |
- x <- append(+ private$check_result <- NULL |
||
110 | -! | +|||
308 | +5x |
- x,+ logger::log_trace( |
||
111 | -! | +|||
309 | +5x |
- list(+ sprintf( |
||
112 | -! | +|||
310 | +5x |
- parent = self$get_parent()+ "TealDataAbstract$mutate code (%s lines) and vars (%s) set for dataset: %s.", |
||
113 | -+ | |||
311 | +5x |
- )+ length(parse(text = code, keep.source = FALSE)), |
||
114 | -+ | |||
312 | +5x |
- )+ paste(names(vars), collapse = ", "), |
||
115 | -! | +|||
313 | +5x |
- return(x)+ dataname |
||
116 | +314 |
- },+ ) |
||
117 | +315 |
- #' @description+ ) |
||
118 | +316 |
- #' Get parent dataset name+ |
||
119 | -+ | |||
317 | +5x |
- #' @return (`character`) indicating parent `dataname`+ return(invisible(self)) |
||
120 | +318 |
- get_parent = function() {+ }, |
||
121 | -38x | +|||
319 | +
- return(private$parent)+ #' @description |
|||
122 | +320 |
- },+ #' Set reproducibility check |
||
123 | +321 |
- #' @description+ #' |
||
124 | +322 |
- #' Set parent dataset name+ #' @param check (`logical`) whether to perform reproducibility check. |
||
125 | +323 |
- #' @param parent (`character`) indicating parent `dataname`+ #' |
||
126 | +324 |
- #' @return (`self`) invisibly for chaining+ #' @return (`self`) invisibly for chaining. |
||
127 | +325 |
- set_parent = function(parent) {+ set_check = function(check = FALSE) { |
||
128 | -81x | +326 | +132x |
- checkmate::assert_character(parent, max.len = 1, any.missing = FALSE)+ checkmate::assert_flag(check) |
129 | -81x | -
- private$parent <- parent- |
- ||
130 | -+ | 327 | +131x |
-
+ private$.check <- check |
131 | -81x | +328 | +131x |
- logger::log_trace("CDISCTealDataset$set_parent parent set for dataset: { deparse1(self$get_dataname()) }.")+ logger::log_trace("TealDataAbstract$set_check check set to: { check }.") |
132 | -81x | +329 | +131x |
return(invisible(self)) |
133 | +330 |
- }+ }, |
||
134 | +331 |
- ),+ #' @description |
||
135 | +332 |
- ## __Private Fields ====+ #' Set pull code |
||
136 | +333 |
- private = list(+ #' |
||
137 | +334 |
- parent = character(0)+ #' @param code (`character` value)\cr |
||
138 | +335 |
- )+ #' code to reproduce `data` in `TealDataset` objects. Can't be set if any dataset |
||
139 | +336 |
- )+ #' has `code` set already. |
||
140 | +337 |
-
+ #' |
||
141 | +338 |
- # constructors ====+ #' @return (`self`) invisibly for chaining. |
||
142 | +339 |
- #' Create a new object of `CDISCTealDataset` class+ set_pull_code = function(code) { |
||
143 | -+ | |||
340 | +7x |
- #'+ checkmate::assert_string(code) |
||
144 | -+ | |||
341 | +6x |
- #' @description `r lifecycle::badge("stable")`+ is_code_set <- vapply( |
||
145 | -+ | |||
342 | +6x |
- #' Function that creates `CDISCTealDataset` object+ self$get_items(), |
||
146 | -+ | |||
343 | +6x |
- #'+ function(item) { |
||
147 | -+ | |||
344 | +11x |
- #' @inheritParams dataset+ get_code(item, deparse = TRUE) != "" |
||
148 | +345 |
- #' @param parent (`character`, optional) parent dataset name+ }, |
||
149 | -+ | |||
346 | +6x |
- #'+ logical(1) |
||
150 | +347 |
- #' @return (`CDISCTealDataset`) a dataset with connected metadata+ ) |
||
151 | +348 |
- #'+ + |
+ ||
349 | +6x | +
+ is_dataset <- vapply(+ |
+ ||
350 | +6x | +
+ self$get_items(),+ |
+ ||
351 | +6x | +
+ function(item) {+ |
+ ||
352 | +11x | +
+ inherits(item, "TealDataset") |
||
152 | +353 |
- #' @export+ },+ |
+ ||
354 | +6x | +
+ logical(1) |
||
153 | +355 |
- #'+ ) |
||
154 | +356 |
- #' @examples+ + |
+ ||
357 | +6x | +
+ if (any(is_code_set & is_dataset)) {+ |
+ ||
358 | +2x | +
+ stop(+ |
+ ||
359 | +2x | +
+ "'code' argument should be specified only in the 'cdisc_data' or in 'cdisc_dataset' but not in both", |
||
155 | -+ | |||
360 | +2x |
- #' ADSL <- example_cdisc_data("ADSL")+ call. = FALSE |
||
156 | +361 |
- #'+ ) |
||
157 | +362 |
- #' cdisc_dataset("ADSL", ADSL, metadata = list(type = "teal.data"))+ } |
||
158 | +363 |
- cdisc_dataset <- function(dataname,+ |
||
159 | -+ | |||
364 | +4x |
- x,+ if (all(!is_dataset)) { |
||
160 | -+ | |||
365 | +1x |
- keys = get_cdisc_keys(dataname),+ stop( |
||
161 | -+ | |||
366 | +1x |
- parent = `if`(identical(dataname, "ADSL"), character(0), "ADSL"),+ "Connectors are reproducible by default and setting 'code' argument might break it", |
||
162 | -+ | |||
367 | +1x |
- label = data_label(x),+ call. = FALSE |
||
163 | +368 |
- code = character(0),+ ) |
||
164 | +369 |
- vars = list(),+ } |
||
165 | +370 |
- metadata = NULL) {+ |
||
166 | -66x | +371 | +3x |
- CDISCTealDataset$new(+ private$pull_code <- private$pull_code$set_code( |
167 | -66x | +372 | +3x |
- dataname = dataname,+ code = code, |
168 | -66x | +373 | +3x |
- x = x,+ dataname = self$get_datanames() |
169 | -66x | +|||
374 | +
- keys = keys,+ ) |
|||
170 | -66x | +375 | +3x |
- parent = parent,+ logger::log_trace("TealDataAbstract$set_pull_code pull code set.") |
171 | -66x | +|||
376 | +
- label = label,+ |
|||
172 | -66x | +377 | +3x |
- code = code,+ return(invisible(self)) |
173 | -66x | +|||
378 | +
- vars = vars,+ }, |
|||
174 | -66x | +|||
379 | +
- metadata = metadata+ |
|||
175 | +380 |
- )+ #' @description |
||
176 | +381 |
- }+ #' Reassign `vars` in `TealDataset` and `TealDatasetConnector` objects |
||
177 | +382 |
-
+ #' to keep the valid reference after deep cloning |
||
178 | +383 |
- #' Load `CDISCTealDataset` object from a file+ #' For example if `TealDatasetConnector` has a dependency on some `TealDataset`, this |
||
179 | +384 |
- #'+ #' `TealDataset` is reassigned inside of `TealDatasetConnector`. |
||
180 | +385 |
- #' @description `r lifecycle::badge("experimental")`+ reassign_datasets_vars = function() { |
||
181 | -+ | |||
386 | +3x |
- #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise.+ for (dataset in self$get_items()) { |
||
182 | -+ | |||
387 | +6x |
- #'+ dataset$reassign_datasets_vars( |
||
183 | -+ | |||
388 | +6x |
- #' @inheritParams dataset_file+ datasets = self$get_items() |
||
184 | +389 |
- #'+ ) |
||
185 | +390 |
- #' @return (`CDISCTealDataset`) object+ }+ |
+ ||
391 | +3x | +
+ logger::log_trace("TealDataAbstract$reassign_datasets_vars reassigned vars.")+ |
+ ||
392 | +3x | +
+ invisible(NULL) |
||
186 | +393 |
- #'+ } |
||
187 | +394 |
- #' @export+ ), |
||
188 | +395 |
- #'+ |
||
189 | +396 |
- #' @examples+ ## __Private Fields ==== |
||
190 | +397 |
- #' # simple example+ private = list( |
||
191 | +398 |
- #' file_example <- tempfile(fileext = ".R")+ datasets = NULL, |
||
192 | +399 |
- #' writeLines(+ .check = FALSE, |
||
193 | +400 |
- #' text = c(+ check_result = NULL, # TRUE / FALSE after calling check() |
||
194 | +401 |
- #' "library(teal.data)+ mutate_code = NULL, # CodeClass after initialization |
||
195 | +402 |
- #' cdisc_dataset(dataname = \"ADSL\",+ mutate_vars = list(), # named list with vars used to mutate object |
||
196 | +403 |
- #' x = teal.data::example_cdisc_data(\"ADSL\"),+ pull_code = NULL, # CodeClass - code to reproduce loading of TealDataset(s) only |
||
197 | +404 |
- #' code = \"ADSL <- teal.data::example_cdisc_data('ADSL')\")"+ |
||
198 | +405 |
- #' ),+ ## __Private Methods ==== |
||
199 | +406 |
- #' con = file_example+ # need to have a custom deep_clone because one of the key fields are reference-type object |
||
200 | +407 |
- #' )+ # in particular: datasets is a list of R6 objects that wouldn't be cloned using default clone(deep = T) |
||
201 | +408 |
- #' x <- cdisc_dataset_file(file_example, code = character(0))+ deep_clone = function(name, value) {+ |
+ ||
409 | +222x | +
+ deep_clone_r6(name, value) |
||
202 | +410 |
- #' get_code(x)+ }, |
||
203 | +411 |
- cdisc_dataset_file <- function(path, code = get_code(path)) {+ check_combined_code = function() { |
||
204 | -! | +|||
412 | +4x |
- object <- object_file(path, "CDISCTealDataset")+ execution_environment <- new.env(parent = parent.env(globalenv())) |
||
205 | -! | +|||
413 | +4x |
- object$set_code(code)+ self$get_code_class(only_pull = TRUE)$eval(envir = execution_environment) |
||
206 | -! | +|||
414 | +4x |
- return(object)+ res <- all(vapply( |
||
207 | -+ | |||
415 | +4x |
- }+ Filter(is_pulled, self$get_items()), |
1 | -+ | |||
416 | +4x |
- #' Get dataset primary keys+ function(dataset) { |
||
2 | -+ | |||
417 | +8x |
- #'+ data <- get_raw_data(dataset) |
||
3 | -+ | |||
418 | +8x |
- #' @description `r lifecycle::badge("stable")`+ data_from_code <- get(get_dataname(dataset), execution_environment) |
||
4 | -+ | |||
419 | +7x |
- #' Get dataset primary keys+ identical(data, data_from_code) |
||
5 | +420 |
- #'+ }, |
||
6 | -+ | |||
421 | +4x |
- #' @param x an object of `TealDataset` or `TealDatasetConnector` class+ logical(1) |
||
7 | +422 |
- #' @param dataname (`character`) name of dataset to return keys for+ )) |
||
8 | -+ | |||
423 | +3x |
- #' @param ... not used, only for support of S3+ logger::log_trace("TealDataAbstract$check_combined_code reproducibility result of the combined code: { res }.") |
||
9 | -+ | |||
424 | +3x |
- #'+ res |
||
10 | +425 |
- #' @return (`character`) vector of column names+ }, |
||
11 | +426 |
- #'+ get_datasets_code_class = function() { |
||
12 | -+ | |||
427 | +78x |
- #' @export+ res <- CodeClass$new()+ |
+ ||
428 | +78x | +
+ if (is.null(private$datasets)) {+ |
+ ||
429 | +! | +
+ return(res) |
||
13 | +430 |
- get_keys <- function(x, ...) {+ } |
||
14 | -92x | +431 | +78x |
- UseMethod("get_keys")+ for (dataset in private$datasets) { |
15 | -+ | |||
432 | +164x |
- }+ res$append(dataset$get_code_class()) |
||
16 | +433 |
-
+ } |
||
17 | -+ | |||
434 | +78x |
- #' @rdname get_keys+ return(res) |
||
18 | +435 |
- #' @export+ }, |
||
19 | +436 |
- #' @examples+ get_mutate_code_class = function() { |
||
20 | -+ | |||
437 | +71x |
- #' # TealDataset --------+ res <- CodeClass$new() |
||
21 | -+ | |||
438 | +71x |
- #'+ res$append(list_to_code_class(private$mutate_vars)) |
||
22 | -+ | |||
439 | +71x |
- #' get_keys(+ res$append(private$mutate_code) |
||
23 | -+ | |||
440 | +71x |
- #' dataset(+ return(res) |
||
24 | +441 |
- #' "ADSL",+ }, |
||
25 | +442 |
- #' teal.data::example_cdisc_data("ADSL"),+ get_pull_code_class = function() { |
||
26 | -+ | |||
443 | +48x |
- #' keys = get_cdisc_keys("ADSL"),+ res <- CodeClass$new() |
||
27 | -+ | |||
444 | +48x |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ res$append(private$pull_code)+ |
+ ||
445 | +48x | +
+ return(res) |
||
28 | +446 |
- #' )+ }, |
||
29 | +447 |
- #' )+ set_mutate_code = function(code, dataname = self$get_datanames(), deps = names(private$mutate_vars)) { |
||
30 | -+ | |||
448 | +16x |
- get_keys.TealDataset <- function(x, ...) {+ checkmate::assert( |
||
31 | -72x | +449 | +16x |
- check_ellipsis(...)+ checkmate::check_character(code, max.len = 1, any.missing = FALSE), |
32 | -72x | +450 | +16x |
- x$get_keys()+ checkmate::check_class(code, "PythonCodeClass") |
33 | +451 |
- }+ ) |
||
34 | +452 | |||
35 | -+ | |||
453 | +14x |
- #' @rdname get_keys+ if (inherits(code, "PythonCodeClass")) { |
||
36 | -+ | |||
454 | +! |
- #' @export+ r <- PythonCodeClass$new() |
||
37 | -+ | |||
455 | +! |
- #' @examples+ r$append(private$mutate_code) |
||
38 | -+ | |||
456 | +! |
- #' # TealDatasetConnector --------+ private$mutate_code <- r |
||
39 | +457 |
- #' library(magrittr)+ |
||
40 | -+ | |||
458 | +! |
- #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>%+ code <- code$get_code() |
||
41 | +459 |
- #' set_args(list(dataname = "ADAE"))+ } |
||
42 | +460 |
- #' get_keys(+ |
||
43 | -+ | |||
461 | +14x |
- #' dataset_connector(+ if (length(code) > 0 && code != "") { |
||
44 | -+ | |||
462 | +14x |
- #' "ADSL",+ private$mutate_code$set_code(code = code, dataname = dataname, deps = deps) |
||
45 | +463 |
- #' pull_fun_adsl,+ } |
||
46 | +464 |
- #' keys = get_cdisc_keys("ADSL"),+ |
||
47 | -+ | |||
465 | +14x |
- #' )+ return(invisible(self)) |
||
48 | +466 |
- #' )+ }, |
||
49 | +467 |
- get_keys.TealDatasetConnector <- function(x, ...) {+ set_mutate_vars = function(vars) { |
||
50 | -20x | +468 | +17x |
- check_ellipsis(...)+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
51 | -20x | +469 | +15x |
- x$get_keys()+ if (length(vars) > 0) { |
52 | -+ | |||
470 | +2x |
- }+ private$mutate_vars <- c( |
||
53 | -+ | |||
471 | +2x |
-
+ private$mutate_vars, |
||
54 | -+ | |||
472 | +2x |
- #' @rdname get_keys+ vars[!names(vars) %in% names(private$mutate_vars)] |
||
55 | +473 |
- #' @export+ ) |
||
56 | +474 |
- #' @examples+ } |
||
57 | +475 |
- #' # TealData --------+ |
||
58 | -+ | |||
476 | +15x |
- #'+ return(invisible(self)) |
||
59 | +477 |
- #' get_keys(+ }, |
||
60 | +478 |
- #' teal_data(+ check_names = function(x) { |
||
61 | -+ | |||
479 | +149x |
- #' dataset("x", data.frame(x1 = 1:10, y1 = 11:20), keys = "x1"),+ if (any(vapply(x, identical, logical(1), y = ""))) { |
||
62 | -+ | |||
480 | +! |
- #' dataset("y", data.frame(x2 = 1:10, y2 = 11:20), keys = "x2")+ stop("Cannot extract some dataset names") |
||
63 | +481 |
- #' ),+ } |
||
64 | -+ | |||
482 | +149x |
- #' "x"+ if (any(duplicated(x))) { |
||
65 | -+ | |||
483 | +1x |
- #' )+ stop("TealDatasets names should be unique") |
||
66 | +484 |
- get_keys.TealDataAbstract <- function(x, dataname, ...) {+ } |
||
67 | -! | +|||
485 | +148x |
- check_ellipsis(...)+ if (any(x %in% self$get_datanames())) { |
||
68 | +486 | ! |
- get_keys(x$get_items(dataname))+ stop("Some datanames already exists") |
|
69 | +487 |
- }+ } |
||
70 | -+ | |||
488 | +148x |
-
+ return(TRUE) |
||
71 | +489 |
-
+ } |
||
72 | +490 |
-
+ ) |
||
73 | +491 |
- #' Set dataset primary keys+ ) |
74 | +1 |
- #'+ ## `CodeClass` ==== |
|
75 | +2 |
- #' @description `r lifecycle::badge("stable")`+ #' |
|
76 | +3 |
- #' Set dataset primary keys+ #' @title Code Class |
|
77 | +4 |
- #'+ #' @keywords internal |
|
78 | +5 |
- #' @param x an object of `TealDataset` or `TealDatasetConnector` class+ #' |
|
79 | +6 |
- #' @param keys optional, (`character`) vector with primary keys+ #' @examples |
|
80 | +7 |
- #' @param dataname (`character`) name of dataset for which set the keys+ #' cc <- teal.data:::CodeClass$new() |
|
81 | +8 |
- #' @param ... not used, only for support of S3+ #' cc$set_code(c("foo <- function() {1}", "foo2 <- function() {2}")) |
|
82 | +9 |
- #'+ #' cc$get_code() |
|
83 | +10 |
- #' @return (`character`) vector of column names+ #' cc$get_code(deparse = FALSE) |
|
84 | +11 |
#' |
|
85 | +12 |
- #' @export+ #' cc$set_code(c("DF <- data.frame(x = 1:10)", "DF$y <- 1"), "DF") |
|
86 | +13 |
- set_keys <- function(x, ...) {- |
- |
87 | -119x | -
- UseMethod("set_keys")+ #' cc$set_code("DF$a <- foo()", "DF") |
|
88 | +14 |
- }+ #' |
|
89 | +15 |
-
+ #' # dependent dataset |
|
90 | +16 |
- #' @rdname set_keys+ #' cc$set_code(c("DF2 <- data.frame(x2 = 1:10)", "DF2$y2 <- DF$y"), "DF2", deps = "DF") |
|
91 | +17 |
- #' @export+ #' |
|
92 | +18 |
- #' @examples+ #' cc$set_code("baz <- function() {2}") |
|
93 | +19 |
- #' # TealDataset --------+ #' cc$set_code("DF2$a <- baz()", "DF2") |
|
94 | +20 |
#' |
|
95 | +21 |
- #' set_keys(+ #' cc$get_code() |
|
96 | +22 |
- #' dataset(+ #' cc$get_code("DF") |
|
97 | +23 |
- #' "DF",+ #' cc$get_code("DF2") |
|
98 | +24 |
- #' data.frame(ID = 1:10, x = runif(10))+ #' |
|
99 | +25 |
- #' ),+ #' |
|
100 | +26 |
- #' keys = c("ID")+ #' x1 <- teal.data:::CodeClass$new() |
|
101 | +27 |
- #' )+ #' x1$set_code("DF <- data.frame(x = 1:10)", "DF") |
|
102 | +28 |
- set_keys.TealDataset <- function(x, keys, ...) {+ #' x1$get_code() |
|
103 | -119x | +||
29 | +
- check_ellipsis(...)+ #' |
||
104 | -119x | +||
30 | +
- x$set_keys(keys)+ #' x2 <- teal.data:::CodeClass$new() |
||
105 | +31 |
- }+ #' x2$set_code(c("DF2 <- data.frame(x2 = 1:10)", "DF2$x2 <- DF$x"), "DF2", deps = "DF") |
|
106 | +32 |
-
+ #' x2$get_code() |
|
107 | +33 |
- #' @rdname set_keys+ #' |
|
108 | +34 |
- #' @export+ #' x <- teal.data:::CodeClass$new() |
|
109 | +35 |
- #' @examples+ #' x$append(x1) |
|
110 | +36 |
- #' # TealDatasetConnector --------+ #' x$append(x2) |
|
111 | +37 |
#' |
|
112 | +38 |
- #' pull_fun <- callable_function(+ #' x$get_code() |
|
113 | +39 |
- #' function() {+ #' x$get_code("DF") |
|
114 | +40 |
- #' data.frame(ID = 1:10, x = runif(10))+ #' x$get_code("DF2") |
|
115 | +41 |
- #' }+ #' x$get_code(c("DF", "DF2")) |
|
116 | +42 |
- #' )+ #' |
|
117 | +43 |
- #' set_keys(+ #' x3 <- teal.data:::CodeClass$new() |
|
118 | +44 |
- #' dataset_connector(+ #' x3$set_code("DF3 <- data.frame(x3 = 1:10) ", "DF3") |
|
119 | +45 |
- #' "DF",+ #' x3$get_code() |
|
120 | +46 |
- #' pull_fun+ #' |
|
121 | +47 |
- #' ),+ #' x$append(x3) |
|
122 | +48 |
- #' keys = c("ID")+ #' x$get_code("DF3") |
|
123 | +49 |
- #' )+ #' |
|
124 | +50 |
- set_keys.TealDatasetConnector <- function(x, keys, ...) {- |
- |
125 | -! | -
- check_ellipsis(...)- |
- |
126 | -! | -
- x$set_keys(keys)+ #' # mutation simulation |
|
127 | +51 |
- }+ #' x$set_code("DF3$x <- foo(DF$x)", "DF3", deps = "DF") |
|
128 | +52 |
-
+ #' x$get_code("DF3") |
|
129 | +53 |
- #' @rdname set_keys+ CodeClass <- R6::R6Class( # nolint |
|
130 | +54 |
- #' @export+ "CodeClass", |
|
131 | +55 |
- #' @examples+ ## __Public Methods ==== |
|
132 | +56 |
- #' # TealData --------+ public = list( |
|
133 | +57 |
- #'+ #' @description |
|
134 | +58 |
- #' set_keys(+ #' `CodeClass` constructor |
|
135 | +59 |
- #' teal_data(+ #' @param code (`character`) vector of code text to be set |
|
136 | +60 |
- #' dataset("x", data.frame(x1 = 1:10, y1 = 11:20), keys = "x1"),+ #' @param dataname optional, (`character`) vector of `datanames` to assign code to. If empty then the code |
|
137 | +61 |
- #' dataset("y", data.frame(x2 = 1:10, y2 = 11:20), keys = "x2")+ #' is considered to be "global" |
|
138 | +62 |
- #' ),+ #' @param deps optional, (`character`) vector of `datanames` that given code depends on |
|
139 | +63 |
- #' "x",+ #' @return object of class `CodeClass` |
|
140 | +64 |
- #' c("x1", "y1")+ initialize = function(code = character(0), dataname = character(0), deps = character(0)) { |
|
141 | -+ | ||
65 | +3518x |
- #' )+ if (length(code) > 0) { |
|
142 | -+ | ||
66 | +13x |
- set_keys.TealDataAbstract <- function(x, dataname, keys, ...) {+ self$set_code(code, dataname, deps) |
|
143 | -! | +||
67 | +
- check_ellipsis(...)+ } |
||
144 | -! | +||
68 | +3518x |
- set_keys(x$get_items(dataname), keys = keys)+ logger::log_trace("CodeClass initialized.") |
|
145 | -! | +||
69 | +3518x |
- return(invisible(x))+ return(invisible(self)) |
|
146 | +70 |
- }+ }, |
1 | +71 |
- setOldClass("JoinKeys")+ #' @description |
||
2 | +72 |
-
+ #' Append `CodeClass` object to a given `CodeClass` object |
||
3 | +73 |
- #' Reproducible data class.+ #' @param x (`CodeClass`) object to be appended |
||
4 | +74 |
- #'+ #' @return changed `CodeClass` object |
||
5 | +75 |
- #' Reproducible data class basing on [`teal.code::qenv-class`].+ append = function(x) { |
||
6 | -+ | |||
76 | +3022x |
- #' Don't interact with slots directly, it is suggested to use methods instead+ stopifnot(inherits(x, "CodeClass")) |
||
7 | -+ | |||
77 | +3022x |
- #' (see `methods(class = "teal_data")`).+ if (length(x$code) > 0) { |
||
8 | -+ | |||
78 | +1458x |
- #'+ for (code_i in x$code) { |
||
9 | -+ | |||
79 | +2782x |
- #' @name teal_data-class+ private$set_code_single(code_i) |
||
10 | +80 |
- #' @rdname teal_data-class+ } |
||
11 | -+ | |||
81 | +1458x |
- #'+ logger::log_trace("CodeClass$append CodeClass appended.") |
||
12 | +82 |
- #' @slot code (`character`) representing code necessary to reproduce the environment.+ } |
||
13 | +83 |
- #' To extract the `code` please use [get_code()].+ |
||
14 | -+ | |||
84 | +3022x |
- #' @slot env (`environment`) environment which content was generated by the evaluation+ return(invisible(self)) |
||
15 | +85 |
- #' of the `code` slot. To extract variables from the environment please use [get_var()] or [`[[`].+ }, |
||
16 | +86 |
- #' @slot id (`integer`) random identifier of the code element to make sure uniqueness+ #' @description |
||
17 | +87 |
- #' when joining.+ #' Set code in form of character |
||
18 | +88 |
- #' @slot warnings (`character`) the warnings output when evaluating the code.+ #' @param code (`character`) vector of code text to be set |
||
19 | +89 |
- #' To extract the `warnings` use [get_warnings()].+ #' @param dataname optional, (`character`) vector of `datanames` to assign code to. If empty then the code |
||
20 | +90 |
- #' @slot messages (`character`) the messages output when evaluating the code+ #' is considered to be "global" |
||
21 | +91 |
- #' @slot join_keys (`JoinKeys`) object.+ #' @param deps optional, (`character`) vector of `datanames` that given code depends on |
||
22 | +92 |
- #' To extract the `join_keys` use [get_join_keys()].+ #' |
||
23 | +93 |
- #' @slot datanames (`character`) names of datasets in `env`. Needed when non-dataset+ #' @return changed `CodeClass` object |
||
24 | +94 |
- #' objects are needed in the `env` slot.+ set_code = function(code, dataname = character(0), deps = character(0)) { |
||
25 | -+ | |||
95 | +793x |
- #' To extract the `datanames` use [get_dataname()].+ checkmate::assert_character(code, min.len = 1, any.missing = FALSE) |
||
26 | -+ | |||
96 | +793x |
- #'+ checkmate::assert_character(dataname, any.missing = FALSE) |
||
27 | -+ | |||
97 | +793x |
- #' @import teal.code+ stopifnot(!(dataname %in% deps)) |
||
28 | +98 |
- #' @keywords internal+ |
||
29 | -+ | |||
99 | +793x |
- setClass(+ code <- pretty_code_string(code) |
||
30 | +100 |
- Class = "teal_data",+ |
||
31 | -+ | |||
101 | +793x |
- contains = "qenv",+ for (code_single in code) { |
||
32 | -+ | |||
102 | +822x |
- slots = c(join_keys = "JoinKeys", datanames = "character"),+ private$set_code_single(code_single, dataname, deps) |
||
33 | +103 |
- prototype = list(+ } |
||
34 | -+ | |||
104 | +793x |
- join_keys = join_keys(),+ logger::log_trace("CodeClass$set_code code set.") |
||
35 | -+ | |||
105 | +793x |
- datanames = character(0)+ return(invisible(self)) |
||
36 | +106 |
- )+ }, |
||
37 | +107 |
- )+ #' @description |
||
38 | +108 |
-
+ #' Get the code for a given data names |
||
39 | +109 |
- #' Initialize `teal_data` object+ #' @param dataname optional, (`character`) vector of `datanames` for which the code is extracted. |
||
40 | +110 |
- #'+ #' If `NULL` then get the code for all data names |
||
41 | +111 |
- #' Initialize `teal_data` object.+ #' @param deparse optional, (`logical`) whether to return the deparsed form of a call |
||
42 | +112 |
- #' @name new_teal_data+ #' @return `character` or `list` of calls |
||
43 | +113 |
- #'+ get_code = function(dataname = NULL, deparse = TRUE) { |
||
44 | -+ | |||
114 | +273x |
- #' @param data (`named list`) List of data.+ checkmate::assert_character(dataname, min.len = 1, null.ok = TRUE, any.missing = FALSE)+ |
+ ||
115 | +273x | +
+ checkmate::assert_flag(deparse)+ |
+ ||
116 | +273x | +
+ if (is.null(dataname)) {+ |
+ ||
117 | +234x | +
+ private$get_code_all(deparse = deparse) |
||
45 | +118 |
- #' @param code (`character` or `language`) code to reproduce the `data`.+ } else {+ |
+ ||
119 | +39x | +
+ private$get_code_dataname(dataname = dataname, deparse = deparse) |
||
46 | +120 |
- #' Accepts and stores comments also.+ } |
||
47 | +121 |
- #' @param keys (`JoinKeys`) object+ }, |
||
48 | +122 |
- #' @param datanames (`character`) names of datasets passed to `data`.+ #' @description |
||
49 | +123 |
- #' Needed when non-dataset objects are needed in the `env` slot.+ #' Evaluates internal code within given environment |
||
50 | +124 |
- #' @rdname new_teal_data+ #' @param envir (`environment`) environment in which code will be evaluated |
||
51 | +125 |
- #' @keywords internal+ #' @return invisibly `NULL` |
||
52 | +126 |
- new_teal_data <- function(data, code = character(0), keys = join_keys(), datanames = names(data)) {+ eval = function(envir = new.env(parent = parent.env(.GlobalEnv))) { |
||
53 | -12x | +127 | +88x |
- checkmate::assert_list(data)+ for (x in self$get_code(deparse = FALSE)) { |
54 | -12x | +128 | +121x |
- checkmate::assert_class(keys, "JoinKeys")+ out <- tryCatch( |
55 | -12x | +129 | +121x |
- checkmate::assert_character(datanames)+ base::eval(x, envir = envir), |
56 | -12x | +130 | +121x |
- if (!any(is.language(code), is.character(code))) {+ error = function(e) e |
57 | -! | +|||
131 | +
- stop("`code` must be a character or language object.")+ ) |
|||
58 | +132 |
- }+ |
||
59 | -12x | +133 | +121x |
- code <- format_expression(code)+ if (inherits(out, "error")) { |
60 | -+ | |||
134 | +4x |
-
+ error_msg <- sprintf( |
||
61 | -12x | +135 | +4x |
- new_env <- rlang::env_clone(list2env(data), parent = parent.env(.GlobalEnv))+ "%s\n\nEvaluation of the code failed:\n %s", deparse1(x, collapse = "\n"), conditionMessage(out) |
62 | -12x | +|||
136 | +
- lockEnvironment(new_env, bindings = TRUE)+ ) |
|||
63 | +137 | |||
64 | -12x | +138 | +4x |
- methods::new(+ rlang::with_options( |
65 | -12x | +139 | +4x |
- "teal_data",+ stop(error_msg, call. = FALSE), |
66 | -12x | +140 | +4x |
- env = new_env,+ warning.length = max(min(8170, nchar(error_msg) + 30), 100) |
67 | -12x | +|||
141 | +
- code = code,+ ) |
|||
68 | -12x | +|||
142 | +
- warnings = "",+ } |
|||
69 | -12x | +|||
143 | +
- messages = "",+ } |
|||
70 | -12x | +144 | +84x |
- id = sample.int(.Machine$integer.max, size = 1L),+ logger::log_trace("CodeClass$eval successfuly evaluated the code.") |
71 | -12x | +145 | +84x |
- join_keys = keys,+ return(invisible(NULL)) |
72 | -12x | +|||
146 | +
- datanames = datanames+ } |
|||
73 | +147 |
- )+ ), |
||
74 | +148 |
- }+ private = list( |
1 | +149 |
- #' Get code from script+ ## __Private Fields ==== |
||
2 | +150 |
- #'+ .code = list(), |
||
3 | +151 |
- #' Get code from script. Switches between `code` and `script` arguments+ deps = list(), |
||
4 | +152 |
- #' to return non-empty one to pass it further to constructors.+ ## __Private Methods ==== |
||
5 | +153 |
- #'+ set_code_single = function(code, |
||
6 | +154 |
- #' @param code (`character`)\cr+ dataname = attr(code, "dataname"), |
||
7 | +155 |
- #' an R code to be evaluated or a `PythonCodeClass` created using [python_code].+ deps = attr(code, "deps"), |
||
8 | +156 |
- #' @inheritParams dataset_connector+ id = attr(code, "id")) {+ |
+ ||
157 | +! | +
+ if (is.null(dataname)) dataname <- character(0)+ |
+ ||
158 | +418x | +
+ if (is.null(deps)) deps <- character(0)+ |
+ ||
159 | +822x | +
+ if (is.null(id)) id <- digest::digest(c(private$.code, code)) |
||
9 | +160 |
- #' @return code (`character`)+ # Line shouldn't be added when it contains the same code and the same `dataname` |
||
10 | +161 |
- #' @keywords internal+ # as a line already present in an object of `CodeClass` |
||
11 | +162 |
- code_from_script <- function(code, script, dataname = NULL) {+ if ( |
||
12 | -249x | +163 | +3604x |
- checkmate::assert(+ !id %in% unlist(lapply(private$.code, "attr", "id")) || |
13 | -249x | +164 | +3604x |
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ all( |
14 | -249x | +165 | +3604x |
- checkmate::check_class(code, "PythonCodeClass")+ vapply(dataname, FUN.VALUE = logical(1), FUN = function(x) {+ |
+
166 | +206x | +
+ !x %in% unlist(lapply(private$.code, "attr", "dataname")) |
||
15 | +167 |
- )+ }) |
||
16 | -247x | +|||
168 | +
- checkmate::assert_character(script, max.len = 1, any.missing = FALSE)+ )+ |
+ |||
169 | ++ |
+ ) { |
||
17 | -247x | +170 | +3399x |
- if (length(code) == 0 && length(script) == 0) {+ attr(code, "dataname") <- dataname |
18 | -182x | +171 | +3399x |
- return(character(0))+ attr(code, "deps") <- deps |
19 | -+ | |||
172 | +3399x |
- }+ attr(code, "id") <- id |
||
20 | +173 | |||
21 | -65x | +174 | +3399x |
- if (checkmate::test_string(code) && checkmate::test_string(script)) {+ private$.code <- base::append(private$.code, list(code)) |
22 | -! | +|||
175 | +
- stop("Function doesn't accept 'code' and 'script' at the same time.+ } |
|||
23 | -! | +|||
176 | +3604x |
- Please specify either 'code' or 'script'", call. = FALSE)+ return(invisible(NULL)) |
||
24 | +177 |
- }+ }, |
||
25 | +178 |
-
+ get_code_all = function(deparse) { |
||
26 | -65x | +179 | +234x |
- if (checkmate::test_string(script)) {+ private$get_code_idx(idx = seq_along(private$.code), deparse = deparse) |
27 | -! | +|||
180 | +
- code <- read_script(file = script, dataname = dataname)+ }, |
|||
28 | +181 |
- }+ get_code_dataname = function(dataname, deparse) { |
||
29 | +182 |
-
+ # the lines of code we need for the dataname |
||
30 | -65x | +183 | +39x |
- code+ res <- integer(0) |
31 | +184 |
- }+ # the set of datanames we want code for code for initially just dataname+ |
+ ||
185 | +39x | +
+ datanames <- dataname |
||
32 | +186 | |||
33 | +187 |
- #' Read .R file into character+ # loop backwards along code |
||
34 | -+ | |||
188 | +39x |
- #'+ for (idx in rev(seq_along(private$.code))) { |
||
35 | -+ | |||
189 | +170x |
- #' @description `r lifecycle::badge("stable")`+ code_entry <- private$.code[[idx]] |
||
36 | +190 |
- #' Comments will be excluded+ |
||
37 | +191 |
- #'+ # line of code is one we want if it is not empty and |
||
38 | +192 |
- #' @param file (`character`) File to be parsed into code+ # has any dataname attribute in the vector datanames or dataname starts with * or is global code and |
||
39 | +193 |
- #' @param dataname (`character`) dataset name to subset code from chunks+ # already have some lines of code selected |
||
40 | +194 |
- #' @return (`character`) vector with the code+ if ( |
||
41 | +195 |
- #'+ ( |
||
42 | -+ | |||
196 | +170x |
- #' @export+ any(datanames %in% attr(code_entry, "dataname")) || |
||
43 | -+ | |||
197 | +170x |
- #' @examples+ any(grepl("^[*]", attr(code_entry, "dataname"))) || |
||
44 | -+ | |||
198 | +170x |
- #' file_example <- tempfile()+ (length(res) > 0 && length(attr(code_entry, "dataname")) == 0) |
||
45 | +199 |
- #' writeLines(c("x <- 2", "#second line comment", "x <- x + 2"), file_example)+ ) && |
||
46 | -+ | |||
200 | +170x |
- #'+ length(code_entry) > 0 |
||
47 | +201 |
- #' read_script(file_example)+ ) { |
||
48 | +202 |
- read_script <- function(file, dataname = NULL) {+ # append to index of code we want |
||
49 | -2x | +203 | +92x |
- checkmate::assert_string(file)+ res <- c(idx, res) |
50 | -2x | +|||
204 | +
- checkmate::assert_file_exists(file)+ |
|||
51 | -2x | +|||
205 | +
- paste(+ # and update datasets we want for preceding code with additional datanames and deps |
|||
52 | -2x | +206 | +92x |
- code_exclude(+ datanames <- unique(c(datanames, attr(code_entry, "dataname"), attr(code_entry, "deps"))) |
53 | -2x | +|||
207 | +
- enclosed_with_dataname(+ } |
|||
54 | -2x | +|||
208 | +
- get_code_single(file, read_sources = TRUE),+ } |
|||
55 | -2x | +209 | +39x |
- dataname = dataname+ private$get_code_idx(idx = res, deparse = deparse) |
56 | +210 |
- ),+ },+ |
+ ||
211 | ++ |
+ get_code_idx = function(idx, deparse) { |
||
57 | -2x | +212 | +273x |
- exclude_comments = TRUE+ if (isFALSE(deparse)) { |
58 | -+ | |||
213 | +107x |
- ),+ return(Filter( |
||
59 | -2x | +214 | +107x |
- collapse = "\n"+ Negate(is.null), |
60 | -+ | |||
215 | +107x |
- )+ unname(unlist(lapply( |
||
61 | -+ | |||
216 | +107x |
- }+ private$.code[idx], |
||
62 | -+ | |||
217 | +107x |
-
+ function(x) sapply(x, function(i) text_to_call(i), simplify = FALSE) |
||
63 | +218 |
- #' Function to get a file out of a package+ ))) |
||
64 | +219 |
- #'+ )) |
||
65 | +220 |
- #' @param pkg (`character`)\cr+ } else { |
||
66 | -+ | |||
221 | +166x |
- #' The name of the package the file should be received from.+ return(paste0(unlist(private$.code[idx]), collapse = "\n")) |
||
67 | +222 |
- #' @param file_name (`character`)\cr+ } |
||
68 | +223 |
- #' The name of the file to be received or path to it starting from+ } |
||
69 | +224 |
- #' the base package path.+ ), |
||
70 | +225 |
- #' @return The path to the file+ |
||
71 | +226 |
- #' @keywords internal+ ## __Active Fields ==== |
||
72 | +227 |
- #' @examples+ active = list( |
||
73 | +228 |
- #' teal.data:::get_package_file("teal.data", "WORDLIST")+ #' @field code (`list`) Derive the code of the dataset. |
||
74 | +229 |
- #' teal.data:::get_package_file("teal.data", "cdisc_datasets/cdisc_datasets.yaml")+ code = function() { |
||
75 | -+ | |||
230 | +4541x |
- get_package_file <- function(pkg = NULL, file_name = NULL) {+ private$.code |
||
76 | -! | +|||
231 | +
- checkmate::assert_string(pkg)+ } |
|||
77 | -! | +|||
232 | +
- checkmate::assert_string(file_name)+ ) |
|||
78 | -! | +|||
233 | +
- base_file <- system.file(file_name, package = pkg)+ ) |
|||
79 | +234 | |||
80 | -! | +|||
235 | +
- if (file.exists(base_file)) {+ |
|||
81 | -! | +|||
236 | +
- return(base_file)+ ## Functions ==== |
|||
82 | +237 |
- } else {+ |
||
83 | -! | +|||
238 | +
- stop(paste("There is no such file:", file_name, "or package:", pkg))+ # Convert named list to `CodeClass` utilizing both `TealDatasetConnector` and `TealDataset` |
|||
84 | +239 |
- }+ list_to_code_class <- function(x) { |
||
85 | -+ | |||
240 | +1112x |
- }+ checkmate::assert_list(x, min.len = 0, names = "unique") |
||
86 | +241 | |||
87 | -+ | |||
242 | +1112x |
- # Function to be used while trying to load the object of specific class from the script.+ res <- CodeClass$new() |
||
88 | +243 |
- object_file <- function(path, class) {+ |
||
89 | -6x | +244 | +1112x |
- checkmate::assert_string(path)+ if (length(x) > 0) { |
90 | -6x | +245 | +163x |
- checkmate::assert_file_exists(path)+ for (var_idx in seq_along(x)) { |
91 | -6x | +246 | +179x |
- checkmate::assert_string(class)+ var_value <- x[[var_idx]] |
92 | -+ | |||
247 | +179x |
-
+ var_name <- names(x)[[var_idx]] |
||
93 | -6x | +248 | +179x |
- lines <- paste0(readLines(path), collapse = "\n")+ if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) { |
94 | -6x | +249 | +172x |
- object <- eval(parse(text = lines, keep.source = FALSE))+ res$append(var_value$get_code_class()) |
95 | -+ | |||
250 | +172x |
-
+ if (var_name != var_value$get_dataname()) { |
||
96 | -6x | +251 | +136x |
- if (!inherits(object, class)) {+ res$set_code( |
97 | -1x | +252 | +136x |
- stop("The object returned from the file is not of ", class, " class.")+ deparse1(call("<-", as.name(var_name), as.name(var_value$get_dataname())), collapse = "\n"),+ |
+
253 | +136x | +
+ dataname = var_value$get_dataname() |
||
98 | +254 |
- }+ )+ |
+ ||
255 | ++ |
+ }+ |
+ ||
256 | ++ |
+ } else { |
||
99 | -5x | +257 | +7x |
- return(object)+ var_code <- deparse1(call("<-", as.name(var_name), var_value), collapse = "\n")+ |
+
258 | +7x | +
+ res$set_code(var_code, var_name) |
||
100 | +259 |
- }+ } |
||
101 | +260 |
-
+ } |
||
102 | +261 |
- #' Check if package can be loaded+ }+ |
+ ||
262 | +1112x | +
+ return(res) |
||
103 | +263 |
- #'+ } |
||
104 | +264 |
- #' @param pckg `character` package name.+ |
||
105 | +265 |
- #' @param msg `character` error message to display if package is not available.+ #' Create call from string |
||
106 | +266 |
#' |
||
107 | +267 |
- #' @return Error or invisible NULL.+ #' @param x (`character`) string containing the code. |
||
108 | +268 |
- #' @keywords internal+ #' |
||
109 | +269 |
- check_pkg_quietly <- function(pckg, msg) {+ #' @return (`call`) object. |
||
110 | -14x | +|||
270 | +
- checkmate::assert_string(pckg)+ #' @keywords internal |
|||
111 | -14x | +|||
271 | +
- checkmate::assert_string(msg)+ text_to_call <- function(x) { |
|||
112 | -14x | +272 | +169x |
- if (!pckg %in% rownames(utils::installed.packages())) {+ parsed <- parse(text = x, keep.source = FALSE) |
113 | -1x | +273 | +169x |
- stop(msg)+ if (length(parsed) == 0) { |
114 | -+ | |||
274 | +4x |
- }+ return(NULL) |
||
115 | +275 |
-
+ } else { |
||
116 | -13x | +276 | +165x |
- invisible(NULL)+ return(as.list(as.call(parsed))[[1]]) |
117 | +277 |
- }+ } |
||
118 | +278 |
-
+ } |
||
119 | +279 | |||
120 | +280 |
- #' validate metadata as a list of length one atomic entries (or NULL)+ #' Format a vector of code into a string |
||
121 | +281 |
- #' @param metadata `object` to be checked+ #' |
||
122 | +282 |
- #' @return `NULL` or throw error+ #' @param code_vector (`character`) vector containing lines of |
||
123 | +283 |
- #' @examples+ #' code to format into a string. |
||
124 | +284 |
#' |
||
125 | +285 |
- #' validate_metadata(NULL)+ #' @return (`character`) string containing the formatted code. |
||
126 | +286 |
- #' validate_metadata(list(A = TRUE, B = 10, C = "test"))+ #' @keywords internal |
||
127 | +287 |
- #' \dontrun{+ pretty_code_string <- function(code_vector) { |
||
128 | +288 |
- #' validate_metadata(list(a = 1:10))+ # in order to remove bad formatting: text -> code -> text |
||
129 | -+ | |||
289 | +812x |
- #' }+ unlist(lapply( |
||
130 | -+ | |||
290 | +812x |
- #'+ code_vector,+ |
+ ||
291 | +812x | +
+ function(code_single) {+ |
+ ||
292 | +814x | +
+ if (length(parse(text = code_single, keep.source = FALSE)) == 0) { |
||
131 | +293 |
- #' @export+ # if string code cannot be passed into expression (e.g. code comment) then pass on the string+ |
+ ||
294 | +11x | +
+ code_single |
||
132 | +295 |
- validate_metadata <- function(metadata) {+ } else { |
||
133 | -720x | +296 | +803x |
- checkmate::assert_list(metadata, any.missing = FALSE, names = "named", null.ok = TRUE)+ vapply( |
134 | -714x | +297 | +803x |
- lapply(names(metadata), function(name) {+ as.list(as.call(parse(text = code_single, keep.source = FALSE))), |
135 | -156x | +298 | +803x |
- checkmate::assert_atomic(metadata[[name]], len = 1, .var.name = name)+ deparse1, |
136 | -+ | |||
299 | +803x |
- })+ character(1), |
||
137 | -710x | +300 | +803x |
- return(NULL)+ collapse = "\n" |
138 | +301 |
- }+ ) |
||
139 | +302 |
-
+ } |
||
140 | +303 |
- #' Resolve the expected bootstrap theme+ } |
||
141 | +304 |
- #' @keywords internal+ )) |
||
142 | +305 |
- get_teal_bs_theme <- function() {- |
- ||
143 | -8x | -
- bs_theme <- getOption("teal.bs_theme")- |
- ||
144 | -8x | -
- if (is.null(bs_theme)) {+ } |
||
145 | -5x | +
1 | +
- NULL+ #' Load data from connection |
|||
146 | -3x | +|||
2 | +
- } else if (!inherits(bs_theme, "bs_theme")) {+ #' |
|||
147 | -2x | +|||
3 | +
- warning("teal.bs_theme has to be of a bslib::bs_theme class, the default shiny bootstrap is used.")+ #' @description `r lifecycle::badge("stable")` |
|||
148 | -2x | +|||
4 | +
- NULL+ #' Load data from connection. Function used on [`TealDatasetConnector`] and |
|||
149 | +5 |
- } else {+ #' [`TealDataset`] to obtain data from connection. |
||
150 | -1x | +|||
6 | +
- bs_theme+ #' |
|||
151 | +7 |
- }+ #' @param x (`TealDatasetConnector` or `TealDataset`) |
||
152 | +8 |
- }+ #' @param args (`NULL` or named `list`)\cr |
||
153 | +9 |
-
+ #' additional dynamic arguments passed to function which loads the data. |
||
154 | +10 |
- #' Format expression to string+ #' @param try (`logical`) whether perform function evaluation inside `try` clause |
||
155 | +11 |
- #' Convert any expression to a single character vector+ #' @param conn Optional (`TealDataConnection`) object required to pull the data. |
||
156 | +12 |
- #' @param code (`language`, `expression`, `character`)+ #' @param ... not used, only for support of S3 |
||
157 | +13 |
- #' @return `character(1)`+ #' |
||
158 | +14 |
- #' @keywords internal+ #' @return `x` with loaded `dataset` object |
||
159 | +15 |
- format_expression <- function(code) {+ #' @export |
||
160 | -12x | +|||
16 | +
- if (is.language(code)) {+ load_dataset <- function(x, ...) { |
|||
161 | -2x | +17 | +121x |
- code <- lang2calls(code)+ UseMethod("load_dataset") |
162 | +18 |
- }- |
- ||
163 | -12x | -
- paste(code, collapse = "\n")+ } |
||
164 | +19 |
- }+ |
||
165 | +20 |
-
+ #' @rdname load_dataset |
||
166 | +21 |
-
+ #' @examples |
||
167 | +22 |
- # convert language object or lists of language objects to list of simple calls+ #' |
||
168 | +23 |
- # @param x `language` object or a list of thereof+ #' # TealDataset -------- |
||
169 | +24 |
- # @return+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
||
170 | +25 |
- # Given a `call`, an `expression`, a list of `call`s or a list of `expression`s,+ #' ADSL_dataset <- dataset("ADSL", x = ADSL) |
||
171 | +26 |
- # returns a list of `calls`.+ #' |
||
172 | +27 |
- # Symbols and atomic vectors (which may get mixed up in a list) are returned wrapped in list.+ #' load_dataset(ADSL_dataset) |
||
173 | +28 |
- #' @keywords internal+ #' @export |
||
174 | +29 |
- lang2calls <- function(x) {+ load_dataset.TealDataset <- function(x, ...) { # nolint |
||
175 | -2x | +30 | +45x |
- if (is.atomic(x) || is.symbol(x)) {+ check_ellipsis(...) |
176 | -! | +|||
31 | +45x |
- return(list(x))+ return(invisible(x$get_dataset())) |
||
177 | +32 |
- }+ } |
||
178 | -2x | +|||
33 | +
- if (is.call(x)) {+ |
|||
179 | -2x | +|||
34 | +
- if (identical(as.list(x)[[1L]], as.symbol("{"))) {+ #' @rdname load_dataset |
|||
180 | -1x | +|||
35 | +
- as.list(x)[-1L]+ #' @examples |
|||
181 | +36 |
- } else {+ #' |
||
182 | -1x | +|||
37 | +
- list(x)+ #' # TealDatasetConnector -------- |
|||
183 | +38 |
- }+ #' |
||
184 | +39 |
- } else {+ #' random_data_connector <- function(dataname) { |
||
185 | -! | +|||
40 | +
- unlist(lapply(x, lang2calls), recursive = FALSE)+ #' fun_dataset_connector( |
|||
186 | +41 |
- }+ #' dataname = dataname, |
||
187 | +42 |
- }+ #' fun = teal.data::example_cdisc_data, |
1 | +43 |
- #' S3 method for getting a `dataname(s)` of+ #' fun_args = list(dataname = dataname), |
||
2 | +44 |
- #' (`TealDataAbstract`, (`TealDatasetConnector` or+ #' ) |
||
3 | +45 |
- #' `TealDataset`) R6 object+ #' } |
||
4 | +46 |
#' |
||
5 | +47 |
- #' @description `r lifecycle::badge("stable")`+ #' adsl <- random_data_connector(dataname = "ADSL") |
||
6 | +48 |
- #'+ #' load_dataset(adsl) |
||
7 | +49 |
- #' @param x (`TealDataAbstract`, `TealDatasetConnector` or+ #' get_dataset(adsl) |
||
8 | +50 |
- #' `TealDataset`) object+ #' |
||
9 | +51 |
- #'+ #' adae <- random_data_connector(dataname = "ADAE") |
||
10 | +52 |
- #' @return `dataname` (`character`) A given name for the dataset(s)+ #' load_dataset(adae) |
||
11 | +53 |
- #' it may not contain spaces+ #' get_dataset(adae) |
||
12 | +54 |
#' @export |
||
13 | +55 |
- get_dataname <- function(x) {+ load_dataset.TealDatasetConnector <- function(x, args = NULL, try = FALSE, conn = NULL, ...) { # nolint |
||
14 | -731x | +56 | +76x |
- UseMethod("get_dataname")+ check_ellipsis(...)+ |
+
57 | +76x | +
+ if (!is.null(conn)) {+ |
+ ||
58 | +! | +
+ stopifnot(inherits(conn, "TealDataConnection")) |
||
15 | +59 |
- }+ + |
+ ||
60 | +! | +
+ conn$open()+ |
+ ||
61 | +! | +
+ conn_obj <- conn$get_conn() |
||
16 | +62 | |||
63 | +! | +
+ x$get_pull_callable()$assign_to_env("conn", conn_obj)+ |
+ ||
17 | +64 |
- #' @rdname get_dataname+ } |
||
18 | +65 |
- #' @export+ + |
+ ||
66 | +76x | +
+ x$pull(args = args, try = try) |
||
19 | +67 |
- get_dataname.TealDataAbstract <- function(x) { # nolint+ |
||
20 | -15x | +68 | +76x |
- return(x$get_datanames())+ return(invisible(x)) |
21 | +69 |
} |
||
22 | +70 | |||
23 | +71 |
- #' @rdname get_dataname+ #' Load datasets |
||
24 | +72 |
- #' @export+ #' |
||
25 | +73 |
- get_dataname.TealDatasetConnector <- function(x) { # nolint+ #' @description `r lifecycle::badge("stable")` |
||
26 | -213x | +|||
74 | +
- return(x$get_dataname())+ #' |
|||
27 | +75 |
- }+ #' @param x ([`TealData`], [`TealDataset`] or [`TealDatasetConnector`]) |
||
28 | +76 |
-
+ #' @param args (`NULL` or named `list`)\cr |
||
29 | +77 |
-
+ #' additional dynamic arguments passed to function which loads the data. Applicable only on [`TealDatasetConnector`]) |
||
30 | +78 |
- #' @rdname get_dataname+ #' @param try (`logical`)\cr |
||
31 | +79 |
- #' @export+ #' whether perform function evaluation inside `try` clause. Applicable only on [`TealDatasetConnector`]) |
||
32 | +80 |
- get_dataname.TealDataset <- function(x) { # nolint+ #' @param ... (not used)\cr |
||
33 | -503x | +|||
81 | +
- return(x$get_dataname())+ #' only for support of S3 |
|||
34 | +82 |
- }+ #' |
||
35 | +83 |
-
+ #' @export |
||
36 | +84 |
- #' @rdname get_dataname+ #' @return If executed in the interactive session shiny app is opened to load the data. If executed in |
||
37 | +85 |
- #' @export+ #' shiny application - it returns shiny server module. |
||
38 | +86 |
- get_dataname.teal_data <- function(x) { # nolint+ load_datasets <- function(x, ...) { |
||
39 | +87 | ! |
- return(x@datanames)+ UseMethod("load_datasets") |
|
40 | +88 |
} |
1 | +89 |
- ## CallableCode ====+ |
|
2 | +90 |
- #'+ #' @rdname load_datasets |
|
3 | +91 |
- #' @title A \code{CallableCode} class of objects+ #' @examples |
|
4 | +92 |
#' |
|
5 | +93 |
- #' @description `r lifecycle::badge("stable")`+ #' # TealDataset ------ |
|
6 | +94 |
- #' Object that stores code to reproduce an object. It includes methods to+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
|
7 | +95 |
- #' get or run the code and return the object.+ #' x <- dataset("ADSL", x = ADSL) |
|
8 | +96 |
#' |
|
9 | +97 |
- CallableCode <- R6::R6Class( # nolint+ #' load_datasets(x) |
|
10 | +98 |
- "CallableCode",+ #' @export |
|
11 | +99 |
- inherit = Callable,+ load_datasets.TealDataset <- function(x, ...) { # nolint+ |
+ |
100 | +! | +
+ check_ellipsis(...)+ |
+ |
101 | +! | +
+ return(invisible(x$get_dataset())) |
|
12 | +102 | ++ |
+ }+ |
+
103 | |||
13 | +104 |
- ## __Public Methods ====+ #' @rdname load_datasets |
|
14 | +105 |
- public = list(+ #' @examples |
|
15 | +106 |
- #' @description+ #' |
|
16 | +107 |
- #' Create a new \code{CallableCode} object+ #' # TealDatasetConnector ------ |
|
17 | +108 |
- #'+ #' random_data_connector <- function(dataname) { |
|
18 | +109 |
- #' @param code (\code{character})\cr+ #' fun_dataset_connector( |
|
19 | +110 |
- #' a string containing R code to reproduce the desired object.+ #' dataname = dataname, |
|
20 | +111 |
- #' @param env (\code{environment})\cr+ #' fun = teal.data::example_cdisc_data, |
|
21 | +112 |
- #' environment where function will be evaluated+ #' fun_args = list(dataname = dataname), |
|
22 | +113 |
- #'+ #' ) |
|
23 | +114 |
- #' @return new \code{CallableCode} object+ #' } |
|
24 | +115 |
- initialize = function(code, env = new.env(parent = parent.env(globalenv()))) {+ #' |
|
25 | -19x | +||
116 | +
- if (!checkmate::test_string(code)) {+ #' adsl <- random_data_connector(dataname = "ADSL") |
||
26 | -! | +||
117 | +
- stop("A string of length one containing the code needed to produce the object must be provided.")+ #' load_datasets(adsl) |
||
27 | +118 |
- }+ #' get_dataset(adsl) |
|
28 | +119 |
-
+ #' |
|
29 | +120 |
- # reposition all library calls in the code so that they are+ #' adae <- random_data_connector(dataname = "ADAE") |
|
30 | +121 |
- # visible in the new env+ #' load_datasets(adae) |
|
31 | -19x | +||
122 | +
- env$library <- function(...) {+ #' get_dataset(adae) |
||
32 | -4x | +||
123 | +
- mc <- match.call()+ #' @export |
||
33 | -4x | +||
124 | +
- mc[[1]] <- quote(base::library)+ load_datasets.TealDatasetConnector <- function(x, args = NULL, try = FALSE, ...) { # nolint |
||
34 | -4x | +||
125 | +! |
- eval(mc, envir = globalenv())+ check_ellipsis(...) |
|
35 | -4x | +||
126 | +! |
- this_env <- parent.frame()+ x$pull(args = args, try = try) |
|
36 | -+ | ||
127 | +! |
-
+ return(invisible(x)) |
|
37 | -4x | +||
128 | +
- if (!identical(this_env, globalenv())) {+ } |
||
38 | -4x | +||
129 | +
- parent.env(this_env) <- parent.env(globalenv())+ |
||
39 | +130 |
- }+ |
|
40 | +131 |
- }+ #' @rdname load_datasets |
|
41 | +132 |
-
+ #' @export |
|
42 | -19x | +||
133 | +
- super$initialize(env = env)+ #' @examples |
||
43 | +134 |
-
+ #' |
|
44 | -19x | +||
135 | +
- private$code <- code+ #' # TealDataConnector -------- |
||
45 | -19x | +||
136 | +
- private$call <- private$get_callable_code(code)+ #' random_data_connector <- function(dataname) { |
||
46 | -15x | +||
137 | +
- logger::log_trace("CallableCode initialized.")+ #' fun_dataset_connector( |
||
47 | +138 |
-
+ #' dataname = dataname, |
|
48 | -15x | +||
139 | +
- return(invisible(self))+ #' fun = teal.data::example_cdisc_data, |
||
49 | +140 |
- },+ #' fun_args = list(dataname = dataname), |
|
50 | +141 |
- #' @description+ #' ) |
|
51 | +142 |
- #' Get sequence of calls from the code supplied to produce the object.+ #' } |
|
52 | +143 |
- #'+ #' |
|
53 | +144 |
- #' @param deparse (\code{logical} value)\cr+ #' adsl <- random_data_connector(dataname = "ADSL") |
|
54 | +145 |
- #' whether to return a deparsed version of call+ #' adrs <- random_data_connector(dataname = "ADRS") |
|
55 | +146 |
- #' @param args (\code{NULL})\cr+ #' |
|
56 | +147 |
- #' available to be consistent with \code{CallableFunction} but are not used to+ #' rdc <- cdisc_data(adsl, adrs) |
|
57 | +148 |
- #' retrieve the call.+ #' \dontrun{ |
|
58 | +149 |
- #'+ #' load_datasets(rdc) |
|
59 | +150 |
- #' @return \code{list} of \code{calls} or \code{character} depending on \code{deparse} argument+ #' } |
|
60 | +151 |
- get_call = function(deparse = TRUE, args = NULL) {+ load_datasets.TealDataConnector <- function(x, ...) { # nolint |
|
61 | -38x | +||
152 | +! |
- checkmate::assert_flag(deparse)+ check_ellipsis(...) |
|
62 | -38x | +||
153 | +! |
- if (!is.null(args)) {+ if (interactive()) { |
|
63 | +154 | ! |
- stop("'args' are not used to retrieve the call.")+ x$launch() |
64 | +155 |
- }+ } else { |
|
65 | -+ | ||
156 | +! |
-
+ return(invisible(x)) |
|
66 | -38x | +||
157 | +
- res <- if (deparse) {+ } |
||
67 | -4x | +||
158 | +
- paste0(vapply(private$call, deparse1, character(1)), collapse = "\n")+ } |
||
68 | +159 |
- } else {+ |
|
69 | -34x | +||
160 | +
- private$call+ #' @rdname load_datasets |
||
70 | +161 |
- }+ #' @export |
|
71 | +162 |
-
+ #' @examples |
|
72 | -38x | +||
163 | +
- return(res)+ #' |
||
73 | +164 |
- }+ #' # TealData -------- |
|
74 | +165 |
- ),+ #' random_data_connector <- function(dataname) { |
|
75 | +166 |
-
+ #' fun_dataset_connector( |
|
76 | +167 |
- ## __Private Fields ====+ #' dataname = dataname, |
|
77 | +168 |
- private = list(+ #' fun = teal.data::example_cdisc_data, |
|
78 | +169 |
- code = NULL,+ #' fun_args = list(dataname = dataname), |
|
79 | +170 |
- ## __Private Methods ====+ #' ) |
|
80 | +171 |
- # @description+ #' } |
|
81 | +172 |
- # Determines whether code is valid and callable. If not then stores error message.+ #' |
|
82 | +173 |
- #+ #' adsl <- random_data_connector(dataname = "ADSL") |
|
83 | +174 |
- # @param code \code{character} string to check+ #' adlb <- random_data_connector(dataname = "ADLB") |
|
84 | +175 |
- #+ #' adrs <- random_data_connector(dataname = "ADRS") |
|
85 | +176 |
- # @return \code{expression} parsed from the supplied code+ #' |
|
86 | +177 |
- #+ #' tc <- cdisc_data(adsl, adlb, adrs) |
|
87 | +178 |
- get_callable_code = function(code) {+ #' \dontrun{ |
|
88 | -19x | +||
179 | +
- expr <- tryCatch(+ #' load_datasets(tc) |
||
89 | -19x | +||
180 | +
- str2expression(code),+ #' } |
||
90 | -19x | +||
181 | +
- error = function(e) {+ load_datasets.TealData <- function(x, ...) { # nolint |
||
91 | -3x | +||
182 | +! |
- private$error_msg <- e$message+ check_ellipsis(...) |
|
92 | -3x | +||
183 | +! |
- private$failed <- TRUE+ if (interactive()) { |
|
93 | -+ | ||
184 | +! |
- }+ x$launch() |
|
94 | +185 |
- )+ } else { |
|
95 | -19x | +||
186 | +! |
- if (length(expr) >= 1 && !private$failed) {+ return(invisible(x)) |
|
96 | -15x | +||
187 | +
- return(expr)+ } |
||
97 | +188 |
- } else {+ } |
|
98 | -4x | +
1 | +
- stop(paste("Code supplied is not valid:", private$error_msg))+ #' Get Label Attributes of Variables in a \code{data.frame} |
|||
99 | +2 |
- }+ #' |
||
100 | +3 |
- }+ #' Variable labels can be stored as a \code{label} attribute for each variable. |
||
101 | +4 |
- )+ #' This functions returns a named character vector with the variable labels |
||
102 | +5 |
- )+ #' (empty sting if not specified) |
||
103 | +6 |
-
+ #' |
||
104 | +7 |
- ## Constructors ====+ #' @param x a \code{data.frame} object |
||
105 | +8 |
-
+ #' @param fill boolean in case the \code{label} attribute does not exist if |
||
106 | +9 |
- #' Create \code{\link{CallableCode}} object+ #' \code{TRUE} the variable names is returned, otherwise \code{NA} |
||
107 | +10 |
#' |
||
108 | +11 |
- #' @description `r lifecycle::badge("stable")`+ #' @source This function was taken 1-1 from |
||
109 | +12 |
- #'+ #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of |
||
110 | +13 |
- #' Create \link{CallableCode} object to execute specific code and get reproducible call.+ #' the dependency tree. |
||
111 | +14 |
#' |
||
112 | +15 |
- #' @param code (\code{character})\cr+ #' @seealso [col_relabel()] [`col_labels<-`] |
||
113 | +16 |
- #' a string containing R code to reproduce the desired object. Please be aware+ #' |
||
114 | +17 |
- #' that objects assigned to temporary environment are locked which means+ #' @return a named character vector with the variable labels, the names |
||
115 | +18 |
- #' that they can't be modified.+ #' correspond to the variable names |
||
116 | +19 |
#' |
||
117 | +20 |
- #' @return \code{CallableCode} object+ #' @export |
||
118 | +21 |
#' |
||
119 | +22 |
- #' @export+ #' @examples |
||
120 | +23 |
- #'+ #' x <- iris |
||
121 | +24 |
- #' @examples+ #' col_labels(x) |
||
122 | +25 |
- #' cf <- callable_code(code = "mtcars")+ #' col_labels(x) <- paste("label for", names(iris)) |
||
123 | +26 |
- #' cf$run()+ #' col_labels(x) |
||
124 | +27 |
- #' cf$get_call()+ col_labels <- function(x, fill = FALSE) { |
||
125 | -+ | |||
28 | +1x |
- callable_code <- function(code) {+ stopifnot(is.data.frame(x)) |
||
126 | -19x | +29 | +1x |
- CallableCode$new(code)+ if (NCOL(x) == 0) { |
127 | -+ | |||
30 | +! |
- }+ return(character()) |
1 | +31 |
- #' Convert a `TealDataset(Connector)` object to a `CDISCTealDataset(Connector)` object+ } |
||
2 | +32 |
- #'+ + |
+ ||
33 | +1x | +
+ y <- Map(function(col, colname) {+ |
+ ||
34 | +4x | +
+ label <- attr(col, "label") |
||
3 | +35 |
- #' Convert a `TealDataset(Connector)` object to a `CDISCTealDataset(Connector)` object+ + |
+ ||
36 | +4x | +
+ if (is.null(label)) {+ |
+ ||
37 | +4x | +
+ if (fill) {+ |
+ ||
38 | +! | +
+ colname |
||
4 | +39 |
- #'+ } else { |
||
5 | -+ | |||
40 | +1x |
- #' @description `r lifecycle::badge("stable")`+ NA_character_ |
||
6 | +41 |
- #'+ } |
||
7 | +42 |
- #' @note If passed a `CDISC`-flavored object it returns the unmodified object.+ } else { |
||
8 | -+ | |||
43 | +! |
- #'+ if (!is.character(label) && !(length(label) == 1)) { |
||
9 | -+ | |||
44 | +! |
- #' @param x an object of `TealDataset` or `TealDatasetConnector` class+ stop("label for variable ", colname, "is not a character string") |
||
10 | +45 |
- #' @inheritParams cdisc_dataset+ } |
||
11 | -+ | |||
46 | +! |
- #'+ as.vector(label) |
||
12 | +47 |
- #' @return (`CDISCTealDataset` or `CDISCTealDatasetConnector`) object+ } |
||
13 | -+ | |||
48 | +1x |
- #'+ }, x, colnames(x)) |
||
14 | +49 |
- #' @export+ + |
+ ||
50 | +1x | +
+ labels <- unlist(y, recursive = FALSE, use.names = TRUE) |
||
15 | +51 |
- as_cdisc <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) {+ |
||
16 | -50x | +52 | +1x |
- if (any(class(x) %in% c("CDISCTealDataset", "CDISCTealDatasetConnector"))) {+ if (!is.character(labels)) { |
17 | -2x | +|||
53 | +! |
- x+ stop("label extraction failed") |
||
18 | +54 |
- } else {+ } |
||
19 | -48x | +|||
55 | +
- UseMethod("as_cdisc")+ |
|||
20 | -+ | |||
56 | +1x |
- }+ labels |
||
21 | +57 |
} |
||
22 | +58 | |||
23 | +59 |
- #' @rdname as_cdisc+ #' Set Label Attributes of All Variables in a \code{data.frame} |
||
24 | +60 |
- #' @export+ #' |
||
25 | +61 |
- #' @examples+ #' Variable labels can be stored as a \code{label} attribute for each variable. |
||
26 | +62 |
- #' # TealDataset --------+ #' This functions sets all non-missing (non-NA) variable labels in a \code{data.frame} |
||
27 | +63 |
#' |
||
28 | +64 |
- #' as_cdisc(+ #' @inheritParams col_labels |
||
29 | +65 |
- #' dataset(+ #' @param value new variable labels, \code{NA} removes the variable label |
||
30 | +66 |
- #' "ADSL",+ #' |
||
31 | +67 |
- #' teal.data::example_cdisc_data("ADSL"),+ #' @source This function was taken 1-1 from |
||
32 | +68 |
- #' keys = get_cdisc_keys("ADSL"),+ #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of |
||
33 | +69 |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ #' the dependency tree. |
||
34 | +70 |
- #' )+ #' |
||
35 | +71 |
- #' )+ #' @seealso [col_labels()] [col_relabel()] |
||
36 | +72 |
- #' as_cdisc(+ #' |
||
37 | +73 |
- #' dataset(+ #' @return modifies the variable labels of \code{x} |
||
38 | +74 |
- #' "ADAE",+ #' |
||
39 | +75 |
- #' teal.data::example_cdisc_data("ADAE"),+ #' @export |
||
40 | +76 |
- #' keys = get_cdisc_keys("ADAE"),+ #' |
||
41 | +77 |
- #' code = "ADAE <- teal.data::example_cdisc_data(\"ADAE\")"+ #' @examples |
||
42 | +78 |
- #' ),+ #' x <- iris |
||
43 | +79 |
- #' parent = "ADSL"+ #' col_labels(x) |
||
44 | +80 |
- #' )+ #' col_labels(x) <- paste("label for", names(iris)) |
||
45 | +81 |
- as_cdisc.TealDataset <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) {+ #' col_labels(x) |
||
46 | -31x | +|||
82 | +
- if (length(get_keys(x)) > 0 || !(get_dataname(x) %in% names(default_cdisc_keys))) {+ #' |
|||
47 | -30x | +|||
83 | +
- cdisc_dataset(+ #' if (interactive()) { |
|||
48 | -30x | +|||
84 | +
- dataname = get_dataname(x),+ #' View(x) # in RStudio data viewer labels are displayed |
|||
49 | -30x | +|||
85 | +
- x = get_raw_data(x),+ #' } |
|||
50 | -30x | +|||
86 | +
- keys = get_keys(x),+ `col_labels<-` <- function(x, value) { |
|||
51 | -30x | +87 | +6x |
- parent = parent,+ stopifnot( |
52 | -30x | +88 | +6x |
- label = get_dataset_label(x),+ is.data.frame(x), |
53 | -30x | +89 | +6x |
- code = x$get_code_class(),+ is.character(value), |
54 | -30x | +90 | +6x |
- metadata = x$get_metadata()+ ncol(x) == length(value) |
55 | +91 |
- )+ ) |
||
56 | +92 |
- } else {+ |
||
57 | -1x | +93 | +6x |
- cdisc_dataset(+ theseq <- if (!is.null(names(value))) names(value) else seq_along(x) |
58 | -1x | +|||
94 | +
- dataname = get_dataname(x),+ # across columns of x |
|||
59 | -1x | +95 | +6x |
- x = get_raw_data(x),+ for (j in theseq) { |
60 | -1x | +96 | +26x |
- parent = parent,+ attr(x[[j]], "label") <- if (!is.na(value[j])) { |
61 | -1x | +97 | +26x |
- label = get_dataset_label(x),+ value[j] |
62 | -1x | +|||
98 | +
- code = x$get_code_class(),+ } else { |
|||
63 | -1x | +|||
99 | +! |
- metadata = x$get_metadata()+ NULL |
||
64 | +100 |
- )+ } |
||
65 | +101 |
} |
||
66 | -- |
- }- |
- ||
67 | +102 | |||
68 | -+ | |||
103 | +6x |
- #' @rdname as_cdisc+ x |
||
69 | +104 |
- #' @export+ } |
||
70 | +105 |
- #' @examples+ |
||
71 | +106 |
- #' # TealDatasetConnector --------+ #' Copy and Change Variable Labels of a \code{data.frame} |
||
72 | +107 |
- #' library(magrittr)+ #' |
||
73 | +108 |
- #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>%+ #' Relabel a subset of the variables |
||
74 | +109 |
- #' set_args(list(dataname = "ADSL"))+ #' |
||
75 | +110 |
- #' as_cdisc(+ #' @inheritParams col_labels<- |
||
76 | +111 |
- #' dataset_connector(+ #' @param ... name-value pairs, where name corresponds to a variable name in |
||
77 | +112 |
- #' "ADSL",+ #' \code{x} and the value to the new variable label |
||
78 | +113 |
- #' pull_fun_adsl,+ #' |
||
79 | +114 |
- #' keys = get_cdisc_keys("ADSL")+ #' @return a copy of \code{x} with changed labels according to \code{...} |
||
80 | +115 |
- #' )+ #' |
||
81 | +116 |
- #' )+ #' @source This function was taken 1-1 from |
||
82 | +117 |
- #'+ #' \href{https://cran.r-project.org/package=formatters}{formatters} package, to reduce the complexity of |
||
83 | +118 |
- #' pull_fun_adae <- callable_function(teal.data::example_cdisc_data) %>%+ #' the dependency tree. |
||
84 | +119 |
- #' set_args(list(dataname = "ADAE"))+ #' |
||
85 | +120 |
- #' as_cdisc(+ #' @seealso [col_labels()] [`col_labels<-`] |
||
86 | +121 |
- #' dataset_connector(+ #' |
||
87 | +122 |
- #' "ADAE",+ #' @export |
||
88 | +123 |
- #' pull_fun_adae,+ #' |
||
89 | +124 |
- #' keys = get_cdisc_keys("ADAE")+ #' @examples |
||
90 | +125 |
- #' ),+ #' x <- col_relabel(iris, Sepal.Length = "Sepal Length of iris flower") |
||
91 | +126 |
- #' parent = "ADSL"+ #' col_labels(x) |
||
92 | +127 |
- #' )+ #' |
||
93 | +128 |
- as_cdisc.TealDatasetConnector <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) {+ col_relabel <- function(x, ...) { |
||
94 | -17x | +|||
129 | +! |
- ds <- tryCatch(+ stopifnot(is.data.frame(x)) |
||
95 | -17x | +|||
130 | +! |
- expr = get_dataset(x),+ if (missing(...)) { |
||
96 | -17x | +|||
131 | +! |
- error = function(e) NULL+ return(x) |
||
97 | +132 |
- )+ } |
||
98 | -17x | +|||
133 | +! |
- if (!is.null(ds)) {+ dots <- list(...) |
||
99 | +134 | ! |
- warning(+ varnames <- names(dots) |
|
100 | +135 | ! |
- "Pulled 'dataset' from 'x' will not be passed to CDISCTealDatasetConnector.+ stopifnot(!is.null(varnames))+ |
+ |
136 | ++ | + | ||
101 | +137 | ! |
- Avoid pulling before conversion."+ map_varnames <- match(varnames, colnames(x)) |
|
102 | +138 |
- )+ + |
+ ||
139 | +! | +
+ if (any(is.na(map_varnames))) {+ |
+ ||
140 | +! | +
+ stop("variables: ", paste(varnames[is.na(map_varnames)], collapse = ", "), " not found") |
||
103 | +141 |
} |
||
104 | +142 | |||
105 | -17x | +|||
143 | +! |
- cdisc_dataset_connector(+ if (any(vapply(dots, Negate(is.character), logical(1)))) { |
||
106 | -17x | +|||
144 | +! |
- dataname = get_dataname(x),+ stop("all variable labels must be of type character") |
||
107 | -17x | +|||
145 | +
- pull_callable = x$get_pull_callable(),+ } |
|||
108 | -17x | +|||
146 | +
- keys = get_keys(x),+ |
|||
109 | -17x | +|||
147 | +! |
- parent = parent,+ for (i in seq_along(map_varnames)) { |
||
110 | -17x | +|||
148 | +! |
- label = get_dataset_label(x),+ attr(x[[map_varnames[[i]]]], "label") <- dots[[i]] |
||
111 | -17x | +|||
149 | +
- vars = x$.__enclos_env__$private$pull_vars,+ } |
|||
112 | -17x | +|||
150 | +
- metadata = x$.__enclos_env__$private$metadata+ |
|||
113 | -+ | |||
151 | +! |
- )+ x |
||
114 | +152 |
}@@ -59563,14 +59727,14 @@ teal.data coverage - 74.87% |
1 |
- #' Set arguments of a `CallableFunction`+ #' Convert a `TealDataset(Connector)` object to a `CDISCTealDataset(Connector)` object |
||
3 |
- #' @description `r lifecycle::badge("stable")`+ #' Convert a `TealDataset(Connector)` object to a `CDISCTealDataset(Connector)` object |
||
4 |
- #' Set arguments of a `CallableFunction`+ #' |
||
5 |
- #'+ #' @description `r lifecycle::badge("stable")` |
||
6 |
- #' @param x `CallableFunction` or `TealDatasetConnector`)+ #' |
||
7 |
- #' @param args (`NULL` or named `list`) dynamic arguments to function+ #' @note If passed a `CDISC`-flavored object it returns the unmodified object. |
||
9 |
- #' @return nothing+ #' @param x an object of `TealDataset` or `TealDatasetConnector` class |
||
10 |
- #' @rdname set_args+ #' @inheritParams cdisc_dataset |
||
11 |
- #' @export+ #' |
||
12 |
- set_args <- function(x, args) {+ #' @return (`CDISCTealDataset` or `CDISCTealDatasetConnector`) object |
||
13 | -14x | +
- UseMethod("set_args")+ #' |
|
14 |
- }+ #' @export |
||
15 |
-
+ as_cdisc <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) { |
||
16 | -+ | 50x |
- #' @rdname set_args+ if (any(class(x) %in% c("CDISCTealDataset", "CDISCTealDatasetConnector"))) { |
17 | -+ | 2x |
- #' @export+ x |
18 |
- #' @examples+ } else { |
||
19 | -+ | 48x |
- #' ## Using CallableFunction+ UseMethod("as_cdisc") |
20 |
- #' fun <- callable_function(example_cdisc_data)+ } |
||
21 |
- #' set_args(fun, list(dataname = "ADSL"))+ } |
||
22 |
- set_args.CallableFunction <- function(x, args) {+ |
||
23 | -13x | +
- x$set_args(args)+ #' @rdname as_cdisc |
|
24 | -13x | +
- return(invisible(x))+ #' @export |
|
25 |
- }+ #' @examples |
||
26 |
-
+ #' # TealDataset -------- |
||
27 |
- #' @rdname set_args+ #' |
||
28 |
- #' @export+ #' as_cdisc( |
||
29 |
- #' @examples+ #' dataset( |
||
30 |
- #' ## Using CallableCode+ #' "ADSL", |
||
31 |
- #' code <- callable_code("example_cdisc_data()")+ #' teal.data::example_cdisc_data("ADSL"), |
||
32 |
- #' set_args(code, list(df = "adsl"))+ #' keys = get_cdisc_keys("ADSL"), |
||
33 |
- set_args.CallableCode <- function(x, args) {+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
||
34 | -! | +
- warning(+ #' ) |
|
35 | -! | +
- "'CallableCode' is unchangable. Ignoring arguments set by 'set_args'",+ #' ) |
|
36 | -! | +
- call. = FALSE+ #' as_cdisc( |
|
37 |
- )+ #' dataset( |
||
38 | -! | +
- return(invisible(x))+ #' "ADAE", |
|
39 |
- }+ #' teal.data::example_cdisc_data("ADAE"), |
||
40 |
-
+ #' keys = get_cdisc_keys("ADAE"), |
||
41 |
- #' @rdname set_args+ #' code = "ADAE <- teal.data::example_cdisc_data(\"ADAE\")" |
||
42 |
- #' @export+ #' ), |
||
43 |
- #' @examples+ #' parent = "ADSL" |
||
44 |
- #' ## Using TealDatasetConnector+ #' ) |
||
45 |
- #' ds <- dataset_connector("x", pull_callable = callable_function(data.frame))+ as_cdisc.TealDataset <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) { |
||
46 | -+ | 31x |
- #' set_args(ds, list(x = 1:5, y = letters[1:5]))+ if (length(get_keys(x)) > 0 || !(get_dataname(x) %in% names(default_cdisc_keys))) { |
47 | -+ | 30x |
- set_args.TealDatasetConnector <- function(x, args) {+ cdisc_dataset( |
48 | -1x | +30x |
- x$set_args(args)+ dataname = get_dataname(x), |
49 | -1x | +30x |
- return(invisible(x))+ x = get_raw_data(x), |
50 | -- |
- }- |
-
1 | -- |
- ## CallablePythonCode ====- |
- |
2 | -- |
- #'- |
- |
3 | -- |
- #' @title A `CallablePythonCode` class of objects- |
- |
4 | -- |
- #' @keywords internal- |
- |
5 | -- |
- #'- |
- |
6 | -+ | 30x |
- CallablePythonCode <- R6::R6Class( # nolint+ keys = get_keys(x), |
7 | -+ | ||
51 | +30x |
-
+ parent = parent, |
|
8 | -+ | ||
52 | +30x |
- ## __Public Methods ====+ label = get_dataset_label(x), |
|
9 | -+ | ||
53 | +30x |
- classname = "CallablePythonCode",+ code = x$get_code_class(), |
|
10 | -+ | ||
54 | +30x |
- inherit = CallableFunction,+ metadata = x$get_metadata() |
|
11 | +55 |
- public = list(+ ) |
|
12 | +56 |
- #' @description+ } else { |
|
13 | -+ | ||
57 | +1x |
- #' Create a new `CallablePythonCode` object+ cdisc_dataset( |
|
14 | -+ | ||
58 | +1x |
- #'+ dataname = get_dataname(x), |
|
15 | -+ | ||
59 | +1x |
- #' @param fun (`function`)\cr+ x = get_raw_data(x), |
|
16 | -+ | ||
60 | +1x |
- #' function to be evaluated in class. Function should be named+ parent = parent, |
|
17 | -+ | ||
61 | +1x |
- #' @param env (\code{environment})\cr+ label = get_dataset_label(x), |
|
18 | -+ | ||
62 | +1x |
- #' environment where the result of python code evaluation are stored+ code = x$get_code_class(), |
|
19 | -+ | ||
63 | +1x |
- #' @return new `CallablePythonCode` object+ metadata = x$get_metadata() |
|
20 | +64 |
- initialize = function(fun, env = new.env(parent = parent.env(globalenv()))) {- |
- |
21 | -! | -
- if (!requireNamespace("reticulate", quietly = TRUE)) {- |
- |
22 | -! | -
- stop("Cannot load package 'reticulate' - please install the package.", call. = FALSE)+ ) |
|
23 | +65 |
- }- |
- |
24 | -! | -
- if (utils::packageVersion("reticulate") < 1.22) {- |
- |
25 | -! | -
- stop("Please upgrade package 'reticulate', teal.data requires version >= 1.22")+ } |
|
26 | +66 |
- }+ } |
|
27 | +67 | ||
28 | -! | -
- super$initialize(fun = fun, env = env)- |
- |
29 | -! | -
- logger::log_trace("CallablePythonCode initialized.")- |
- |
30 | -! | -
- return(invisible(self))- |
- |
31 | -- |
- },- |
- |
32 | -- |
- #' @description- |
- |
33 | -- |
- #' For scripts and code that contain multiple objects, save the name- |
- |
34 | +68 |
- #' of the object that corresponds to the final dataset of interest.+ #' @rdname as_cdisc |
|
35 | +69 |
- #' This is required for running python scripts with `reticulate`.+ #' @export |
|
36 | +70 |
- #'+ #' @examples |
|
37 | +71 |
- #' @param x (`character`) the name of the object produced by the code+ #' # TealDatasetConnector -------- |
|
38 | +72 |
- #' or script.+ #' library(magrittr) |
|
39 | +73 |
- #'+ #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>% |
|
40 | +74 |
- #' @return (`self`) invisibly for chaining.+ #' set_args(list(dataname = "ADSL")) |
|
41 | +75 |
- set_object = function(x) {- |
- |
42 | -! | -
- private$object <- x- |
- |
43 | -! | -
- private$refresh()+ #' as_cdisc( |
|
44 | -! | +||
76 | +
- logger::log_trace("CallablePythonCode$set_object object set.")+ #' dataset_connector( |
||
45 | -! | +||
77 | +
- return(invisible(self))+ #' "ADSL", |
||
46 | +78 |
- },+ #' pull_fun_adsl, |
|
47 | +79 |
- #' @description+ #' keys = get_cdisc_keys("ADSL") |
|
48 | +80 |
- #' Execute `Callable` python code.+ #' ) |
|
49 | +81 |
- #'+ #' ) |
|
50 | +82 |
- #' @param args (`NULL` or named `list`)\cr+ #' |
|
51 | +83 |
- #' supplied for callable functions only, these are dynamic arguments passed to+ #' pull_fun_adae <- callable_function(teal.data::example_cdisc_data) %>% |
|
52 | +84 |
- #' `reticulate::py_run_string` or `reticulate::py_run_file`. Dynamic arguments+ #' set_args(list(dataname = "ADAE")) |
|
53 | +85 |
- #' are executed in this call and are not saved which means that `self$get_call()`+ #' as_cdisc( |
|
54 | +86 |
- #' won't include them later.+ #' dataset_connector( |
|
55 | +87 |
- #' @param try (`logical` value)\cr+ #' "ADAE", |
|
56 | +88 |
- #' whether perform function evaluation inside `try` clause+ #' pull_fun_adae, |
|
57 | +89 |
- #'+ #' keys = get_cdisc_keys("ADAE") |
|
58 | +90 |
- #' @return nothing or output from function depending on `return`+ #' ), |
|
59 | +91 |
- #' argument. If `run` fails it will return object of class `simple-error` error+ #' parent = "ADSL" |
|
60 | +92 |
- #' when `try = TRUE` or will stop if `try = FALSE`.+ #' ) |
|
61 | +93 |
- run = function(args = NULL, try = FALSE) {+ as_cdisc.TealDatasetConnector <- function(x, parent = `if`(identical(get_dataname(x), "ADSL"), character(0), "ADSL")) { |
|
62 | -! | +||
94 | +17x |
- rlang::with_options(+ ds <- tryCatch( |
|
63 | -! | +||
95 | +17x |
- res <- super$run(args = args, try = try),+ expr = get_dataset(x), |
|
64 | -! | +||
96 | +17x |
- reticulate.engine.environment = private$env+ error = function(e) NULL |
|
65 | +97 |
- )+ ) |
|
66 | -! | +||
98 | +17x |
- if (is.null(res)) {+ if (!is.null(ds)) { |
|
67 | +99 | ! |
- stop("The specified python object returned NULL or does not exist in the python code")+ warning( |
68 | -+ | ||
100 | +! |
- }+ "Pulled 'dataset' from 'x' will not be passed to CDISCTealDatasetConnector. |
|
69 | +101 | ! |
- res+ Avoid pulling before conversion." |
70 | +102 |
- }+ ) |
|
71 | +103 |
- ),+ } |
|
72 | +104 | ||
73 | -+ | ||
105 | +17x |
- ## __Private Fields ====+ cdisc_dataset_connector( |
|
74 | -+ | ||
106 | +17x |
- private = list(+ dataname = get_dataname(x), |
|
75 | -+ | ||
107 | +17x |
- object = NULL,+ pull_callable = x$get_pull_callable(), |
|
76 | -+ | ||
108 | +17x |
-
+ keys = get_keys(x), |
|
77 | -+ | ||
109 | +17x |
- ## __Private Methods ====+ parent = parent, |
|
78 | -+ | ||
110 | +17x |
- # @description+ label = get_dataset_label(x), |
|
79 | -+ | ||
111 | +17x |
- # Refresh call with function name and saved arguments+ vars = x$.__enclos_env__$private$pull_vars, |
|
80 | -+ | ||
112 | +17x |
- #+ metadata = x$.__enclos_env__$private$metadata |
|
81 | +113 |
- # @return nothing+ ) |
|
82 | +114 |
- refresh = function() {+ } |
83 | +1 |
- # replaced str2lang found at:+ #' Get dataset label attribute |
|
84 | +2 |
- # https://rlang.r-lib.org/reference/call2.html+ #' |
|
85 | -! | +||
3 | +
- private$call <- as.call(+ #' @description `r lifecycle::badge("stable")` |
||
86 | -! | +||
4 | +
- c(rlang::parse_expr(private$fun_name), private$args)+ #' |
||
87 | +5 |
- )+ #' @param data \code{data.frame} from which attribute is extracted |
|
88 | +6 |
-
+ #' |
|
89 | -! | +||
7 | +
- private$call <- rlang::parse_expr(+ #' @return (\code{character}) label or \code{NULL} if it is missing |
||
90 | -! | +||
8 | +
- sprintf("%s[[%s]]", deparse1(private$call, collapse = "\n"), deparse1(private$object, collapse = "\n"))+ #' |
||
91 | +9 |
- )+ #' @export |
|
92 | +10 |
- }+ #' |
|
93 | +11 |
- )+ #' @examples |
|
94 | +12 |
- )+ #' data_label(example_cdisc_data("ADSL")) |
|
95 | +13 |
- ## PythonCodeClass ====+ data_label <- function(data) { |
|
96 | -+ | ||
14 | +179x |
- #'+ attr(data, "label") |
|
97 | +15 |
- #' @title A `CallablePythonCode` class of objects+ } |
|
98 | +16 |
- #' @description `r lifecycle::badge("experimental")`+ |
|
99 | +17 |
- #'+ #' Set dataset label attribute |
|
100 | +18 |
- PythonCodeClass <- R6::R6Class( # nolint+ #' |
|
101 | +19 |
- classname = "PythonCodeClass",+ #' @description `r lifecycle::badge("stable")` |
|
102 | +20 |
- inherit = CodeClass,+ #' |
|
103 | +21 |
-
+ #' @param x \code{data.frame} for which attribute is set |
|
104 | +22 |
- ## __Public Methods ====+ #' @param value (\code{character}) label |
|
105 | +23 |
- public = list(+ #' |
|
106 | +24 |
- #' @description+ #' @return modified \code{x} object |
|
107 | +25 |
- #' Evaluates internal code within environment+ #' |
|
108 | +26 |
- #'+ #' @export |
|
109 | +27 |
- #' @param vars (named `list`) additional pre-requisite vars to execute code+ #' |
|
110 | +28 |
- #' @param dataname (`character`) name of the data frame object to be returned+ #' @examples |
|
111 | +29 |
- #' @param envir (`environment`) environment in which code will be evaluated+ #' x <- teal.data::example_cdisc_data("ADSL") |
|
112 | +30 |
- #'+ #' data_label(x) <- "My custom label" |
|
113 | +31 |
- #' @return `data.frame` containing the mutated dataset+ #' data_label(x) |
|
114 | +32 |
- eval = function(vars = list(), dataname = NULL, envir = new.env(parent = parent.env(.GlobalEnv))) {+ `data_label<-` <- function(x, value) { # nolint |
|
115 | +33 | ! |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ stopifnot(is.data.frame(x)) |
116 | +34 | ! |
- execution_environment <- envir+ checkmate::assert_string(value) |
117 | +35 | ||
118 | +36 | ! |
- for (vars_idx in seq_along(vars)) {+ attr(x, "label") <- value |
119 | +37 | ! |
- var_name <- names(vars)[[vars_idx]]+ x |
120 | -! | +||
38 | +
- var_value <- vars[[vars_idx]]+ } |
||
121 | -! | +||
39 | +
- if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) {+ |
||
122 | -! | +||
40 | +
- var_value <- get_raw_data(var_value)+ #' Function that returns the default keys for a `CDISC` dataset by name |
||
123 | +41 |
- }+ #' |
|
124 | -! | +||
42 | +
- assign(envir = execution_environment, x = var_name, value = var_value)+ #' @description `r lifecycle::badge("stable")` |
||
125 | +43 |
- }+ #' |
|
126 | +44 |
-
+ #' @param dataname name of the `CDISC` dataset |
|
127 | +45 |
- # execute+ #' |
|
128 | -! | +||
46 | +
- rlang::with_options(+ #' @return \code{keys} object |
||
129 | -! | +||
47 | +
- super$eval(envir = execution_environment),+ #' |
||
130 | -! | +||
48 | +
- reticulate.engine.environment = execution_environment+ #' @export |
||
131 | +49 |
- )+ #' |
|
132 | +50 |
-
+ #' @examples |
|
133 | +51 |
- # return early if only executing and not grabbing the dataset+ #' get_cdisc_keys("ADSL") |
|
134 | -! | +||
52 | +
- if (is.null(dataname)) {+ get_cdisc_keys <- function(dataname) { |
||
135 | -! | +||
53 | +371x |
- return(as.environment(as.list(execution_environment)))+ checkmate::assert_string(dataname) |
|
136 | +54 |
- }+ |
|
137 | -+ | ||
55 | +371x |
-
+ if (!(dataname %in% names(default_cdisc_keys))) {+ |
+ |
56 | +! | +
+ stop(paste(sprintf("get_cdisc_keys does not support datasets called %s", dataname),+ |
+ |
57 | +! | +
+ " Please specify the keys directly, for example:", |
|
138 | +58 | ! |
- if (!is.data.frame(execution_environment[[dataname]])) {+ sprintf( |
139 | +59 | ! |
- out_msg <- sprintf(+ " cdisc_dataset(dataname = \"%s\", keys = c(\"STUDYID\", \"USUBJID\", ...), parent = \"ADSL\", ...)", |
140 | +60 | ! |
- "\n%s\n\n - Code from %s needs to return a data.frame assigned to an object of dataset name.",+ dataname |
141 | -! | +||
61 | +
- self$get_code(),+ ), |
||
142 | +62 | ! |
- self$get_dataname()+ sep = "\n" |
143 | +63 |
- )+ )) |
|
144 | +64 |
-
+ } else { |
|
145 | -! | +||
65 | +371x |
- rlang::with_options(+ cdisc_keys <- default_cdisc_keys[[dataname]]$primary |
|
146 | -! | +||
66 | +
- .expr = stop(out_msg, call. = FALSE),+ |
||
147 | -! | +||
67 | +371x |
- warning.length = max(min(8170, nchar(out_msg) + 30), 100)+ return(cdisc_keys) |
|
148 | +68 |
- )+ } |
|
149 | +69 |
- }+ } |
|
150 | +70 | ||
151 | -! | -
- new_set <- execution_environment[[dataname]]- |
- |
152 | -! | -
- logger::log_trace("PythonCodeClass$eval successfuly evaluated the code.")- |
- |
153 | +71 |
-
+ #' Extracts dataset and variable labels from a dataset. |
|
154 | -! | +||
72 | +
- return(new_set)+ #' |
||
155 | +73 |
- }+ #' @description `r lifecycle::badge("stable")` |
|
156 | +74 |
- )+ #' |
|
157 | +75 |
- )+ #' @param data (`data.frame`) table to extract the labels from |
|
158 | +76 |
-
+ #' @param fill (`logical(1)`) if `TRUE`, the function will return variable names for columns with non-existent labels; |
|
159 | +77 |
- #' Python Code+ #' otherwise will return `NA` for them |
|
160 | +78 |
#' |
|
161 | +79 |
- #' `r lifecycle::badge("experimental")`+ #' @return `list` with two keys: `dataset_labels` and `column_labels` |
|
162 | +80 |
- #' Create a python code object directly from python code or a+ #' |
|
163 | +81 |
- #' script containing python code.+ #' @export |
|
164 | +82 |
#' |
|
165 | +83 |
- #' @details+ #' @examples |
|
166 | +84 |
- #' Used to mutate dataset connector objects with python code. See+ #' iris_with_labels <- iris |
|
167 | +85 |
- #' [`mutate_dataset`] or [`mutate_data`] for details.+ #' attr(iris_with_labels, "label") <- "Custom iris dataset with labels" |
|
168 | +86 |
- #'+ #' attr(iris_with_labels["Sepal.Length"], "label") <- c(`Sepal.Length` = "Sepal Length") |
|
169 | +87 |
- #' @param code (`character`)\cr+ #' get_labels(iris_with_labels, fill = TRUE) |
|
170 | +88 |
- #' Code to mutate the dataset. Must contain the `dataset$dataname`.+ #' get_labels(iris_with_labels, fill = FALSE) |
|
171 | +89 |
- #' @param script (`character`)\cr+ get_labels <- function(data, fill = TRUE) { |
|
172 | -+ | ||
90 | +8x |
- #' file that contains python Code that can be read using `reticulate::py_run_script`.+ stopifnot(is.data.frame(data)) |
|
173 | -+ | ||
91 | +8x |
- #'+ checkmate::assert_flag(fill) |
|
174 | +92 |
- #' @return (`PythonCodeClass`) object containing python code+ |
|
175 | -+ | ||
93 | +8x |
- #' @export+ column_labels <- Map(function(col, colname) { |
|
176 | -+ | ||
94 | +27x |
- #'+ label <- attr(col, "label") |
|
177 | -+ | ||
95 | +27x |
- #' @examples+ if (is.null(label)) { |
|
178 | -+ | ||
96 | +25x |
- #' \dontrun{+ if (fill) { |
|
179 | -+ | ||
97 | +20x |
- #' library(reticulate)+ colname |
|
180 | +98 |
- #' library(magrittr)+ } else { |
|
181 | -+ | ||
99 | +8x |
- #'+ NA_character_ |
|
182 | +100 |
- #' # mutate dataset object+ } |
|
183 | +101 |
- #'+ } else { |
|
184 | -+ | ||
102 | +2x |
- #' random_data_connector <- function(dataname) {+ if (!checkmate::test_string(label, na.ok = TRUE)) { |
|
185 | -+ | ||
103 | +! |
- #' fun_dataset_connector(+ stop("label for variable ", colname, " is not a character string") |
|
186 | +104 |
- #' dataname = dataname,+ } |
|
187 | -+ | ||
105 | +2x |
- #' fun = teal.data::example_cdisc_data,+ as.vector(label) # because label might be a named vector |
|
188 | +106 |
- #' fun_args = list(dataname = dataname),+ } |
|
189 | -+ | ||
107 | +8x |
- #' )+ }, data, colnames(data)) |
|
190 | -+ | ||
108 | +8x |
- #' }+ column_labels <- unlist(column_labels, recursive = FALSE, use.names = TRUE) |
|
191 | +109 |
- #' x <- random_data_connector(dataname = "ADSL")+ |
|
192 | -+ | ||
110 | +8x |
- #'+ list("dataset_label" = data_label(data), "column_labels" = column_labels) |
|
193 | +111 |
- #' x %>% mutate_dataset(python_code("import pandas as pd+ } |
194 | +1 |
- #' r.ADSL = pd.DataFrame({'x': [1]})"))+ ## CallableCode ==== |
|
195 | +2 |
#' |
|
196 | +3 |
- #' x$get_code()+ #' @title A \code{CallableCode} class of objects |
|
197 | +4 |
- #' x$pull()+ #' |
|
198 | +5 |
- #' x$get_raw_data()+ #' @description `r lifecycle::badge("stable")` |
|
199 | +6 |
- #'+ #' Object that stores code to reproduce an object. It includes methods to |
|
200 | +7 |
- #' # mutate data object+ #' get or run the code and return the object. |
|
201 | +8 |
#' |
|
202 | +9 |
- #' y <- 8+ CallableCode <- R6::R6Class( # nolint |
|
203 | +10 |
- #' tc <- cdisc_data(+ "CallableCode", |
|
204 | +11 |
- #' random_data_connector(dataname = "ADSL"),+ inherit = Callable, |
|
205 | +12 |
- #' random_data_connector(dataname = "ADLB")+ |
|
206 | +13 |
- #' )+ ## __Public Methods ==== |
|
207 | +14 |
- #'+ public = list( |
|
208 | +15 |
- #' tc %>% mutate_data(python_code("import pandas as pd+ #' @description |
|
209 | +16 |
- #' r.ADSL = pd.DataFrame({'STUDYID': [r.y], 'USUBJID': [r.y]})"), vars = list(y = y))+ #' Create a new \code{CallableCode} object |
|
210 | +17 |
- #'+ #' |
|
211 | +18 |
- #'+ #' @param code (\code{character})\cr |
|
212 | +19 |
- #' load_datasets(tc) # submit all+ #' a string containing R code to reproduce the desired object. |
|
213 | +20 |
- #' ds <- tc$get_dataset("ADSL")+ #' @param env (\code{environment})\cr |
|
214 | +21 |
- #' ds$get_raw_data()+ #' environment where function will be evaluated |
|
215 | +22 |
- #' }+ #' |
|
216 | +23 |
- python_code <- function(code = character(0), script = character(0)) {+ #' @return new \code{CallableCode} object |
|
217 | -! | +||
24 | +
- if (!xor(missing(code), missing(script))) stop("Exactly one of 'code' and 'script' is required")+ initialize = function(code, env = new.env(parent = parent.env(globalenv()))) { |
||
218 | -+ | ||
25 | +19x |
-
+ if (!checkmate::test_string(code)) { |
|
219 | +26 | ! |
- if (length(script) > 0) {+ stop("A string of length one containing the code needed to produce the object must be provided.") |
220 | -! | +||
27 | +
- code <- deparse(call("py_run_file", script))+ } |
||
221 | +28 |
- } else {+ |
|
222 | -! | +||
29 | +
- code <- deparse(call("py_run_string", code))+ # reposition all library calls in the code so that they are |
||
223 | +30 |
- }+ # visible in the new env |
|
224 | -! | +||
31 | +19x |
- py <- PythonCodeClass$new()+ env$library <- function(...) { |
|
225 | -! | +||
32 | +4x |
- py$set_code(code)+ mc <- match.call() |
|
226 | -+ | ||
33 | +4x |
-
+ mc[[1]] <- quote(base::library) |
|
227 | -! | +||
34 | +4x |
- return(py)+ eval(mc, envir = globalenv()) |
|
228 | -+ | ||
35 | +4x |
- }+ this_env <- parent.frame() |
1 | +36 |
- #' Get a [`TealDataset`] objects.+ |
||
2 | -+ | |||
37 | +4x |
- #'+ if (!identical(this_env, globalenv())) { |
||
3 | -+ | |||
38 | +4x |
- #' @description `r lifecycle::badge("stable")`+ parent.env(this_env) <- parent.env(globalenv()) |
||
4 | +39 |
- #'+ } |
||
5 | +40 |
- #' @param x ([`TealData`])\cr+ } |
||
6 | +41 |
- #' object containing datasets.+ |
||
7 | -+ | |||
42 | +19x |
- #' @export+ super$initialize(env = env) |
||
8 | +43 |
- #' @return `list` or `TealDataset` objects+ |
||
9 | -+ | |||
44 | +19x |
- get_datasets <- function(x) {+ private$code <- code |
||
10 | -9x | +45 | +19x |
- UseMethod("get_datasets")+ private$call <- private$get_callable_code(code) |
11 | -+ | |||
46 | +15x |
- }+ logger::log_trace("CallableCode initialized.") |
||
12 | +47 | |||
13 | -+ | |||
48 | +15x |
- #' @rdname get_datasets+ return(invisible(self)) |
||
14 | +49 |
- #' @export+ }, |
||
15 | +50 |
- #' @examples+ #' @description |
||
16 | +51 |
- #'+ #' Get sequence of calls from the code supplied to produce the object. |
||
17 | +52 |
- #' library(magrittr)+ #' |
||
18 | +53 |
- #'+ #' @param deparse (\code{logical} value)\cr |
||
19 | +54 |
- #' # TealData --------+ #' whether to return a deparsed version of call |
||
20 | +55 |
- #' adsl <- cdisc_dataset(+ #' @param args (\code{NULL})\cr |
||
21 | +56 |
- #' dataname = "ADSL",+ #' available to be consistent with \code{CallableFunction} but are not used to |
||
22 | +57 |
- #' x = teal.data::example_cdisc_data("ADSL"), ,+ #' retrieve the call. |
||
23 | +58 |
- #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ #' |
||
24 | +59 |
- #' )+ #' @return \code{list} of \code{calls} or \code{character} depending on \code{deparse} argument |
||
25 | +60 |
- #'+ get_call = function(deparse = TRUE, args = NULL) { |
||
26 | -+ | |||
61 | +38x |
- #' adae <- cdisc_dataset(+ checkmate::assert_flag(deparse) |
||
27 | -+ | |||
62 | +38x |
- #' dataname = "ADAE",+ if (!is.null(args)) { |
||
28 | -+ | |||
63 | +! |
- #' x = teal.data::example_cdisc_data("ADAE"),+ stop("'args' are not used to retrieve the call.") |
||
29 | +64 |
- #' code = "library(teal.data)\nADAE <- teal.data::example_cdisc_data(\"ADAE\")"+ } |
||
30 | +65 |
- #' )+ |
||
31 | -+ | |||
66 | +38x |
- #'+ res <- if (deparse) { |
||
32 | -+ | |||
67 | +4x |
- #' rd <- cdisc_data(adsl, adae)+ paste0(vapply(private$call, deparse1, character(1)), collapse = "\n") |
||
33 | +68 |
- #' get_datasets(rd)+ } else { |
||
34 | -+ | |||
69 | +34x |
- #'+ private$call |
||
35 | +70 |
- #' # TealDataConnector --------+ } |
||
36 | +71 |
- #' random_data_connector <- function(dataname) {+ |
||
37 | -+ | |||
72 | +38x |
- #' fun_dataset_connector(+ return(res) |
||
38 | +73 |
- #' dataname = dataname,+ } |
||
39 | +74 |
- #' fun = teal.data::example_cdisc_data,+ ), |
||
40 | +75 |
- #' fun_args = list(dataname = dataname),+ |
||
41 | +76 |
- #' )+ ## __Private Fields ==== |
||
42 | +77 |
- #' }+ private = list( |
||
43 | +78 |
- #'+ code = NULL, |
||
44 | +79 |
- #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>%+ ## __Private Methods ==== |
||
45 | +80 |
- #' set_args(list(dataname = "ADSL"))+ # @description |
||
46 | +81 |
- #' adsl <- cdisc_dataset_connector(+ # Determines whether code is valid and callable. If not then stores error message. |
||
47 | +82 |
- #' dataname = "ADSL",+ # |
||
48 | +83 |
- #' pull_callable = adsl_cf,+ # @param code \code{character} string to check |
||
49 | +84 |
- #' keys = get_cdisc_keys("ADSL")+ # |
||
50 | +85 |
- #' )+ # @return \code{expression} parsed from the supplied code |
||
51 | +86 |
- #' adlb_cf <- callable_function(teal.data::example_cdisc_data) %>%+ # |
||
52 | +87 |
- #' set_args(list(dataname = "ADLB"))+ get_callable_code = function(code) { |
||
53 | -+ | |||
88 | +19x |
- #' adlb <- cdisc_dataset_connector(+ expr <- tryCatch( |
||
54 | -+ | |||
89 | +19x |
- #' dataname = "ADLB",+ str2expression(code), |
||
55 | -+ | |||
90 | +19x |
- #' pull_callable = adlb_cf,+ error = function(e) { |
||
56 | -+ | |||
91 | +3x |
- #' keys = get_cdisc_keys("ADLB")+ private$error_msg <- e$message |
||
57 | -+ | |||
92 | +3x |
- #' )+ private$failed <- TRUE |
||
58 | +93 |
- #'+ } |
||
59 | +94 |
- #' rdc <- relational_data_connector(+ ) |
||
60 | -+ | |||
95 | +19x |
- #' connection = data_connection(),+ if (length(expr) >= 1 && !private$failed) { |
||
61 | -+ | |||
96 | +15x |
- #' connectors = list(adsl, adlb)+ return(expr) |
||
62 | +97 |
- #' )+ } else { |
||
63 | -+ | |||
98 | +4x |
- #'+ stop(paste("Code supplied is not valid:", private$error_msg)) |
||
64 | +99 |
- #' rdc$set_ui(function(id, connection, connectors) p("Example UI"))+ } |
||
65 | +100 |
- #' rdc$set_server(+ } |
||
66 | +101 |
- #' function(id, connection, connectors) {+ ) |
||
67 | +102 |
- #' moduleServer(+ ) |
||
68 | +103 |
- #' id = id,+ |
||
69 | +104 |
- #' module = function(input, output, session) {+ ## Constructors ==== |
||
70 | +105 |
- #' # Note this is simplified as we are not opening a real connection here+ |
||
71 | +106 |
- #' for (connector in connectors) {+ #' Create \code{\link{CallableCode}} object |
||
72 | +107 |
- #' set_args(connector, args = list(name = input$name))+ #' |
||
73 | +108 |
- #' # pull each dataset+ #' @description `r lifecycle::badge("stable")` |
||
74 | +109 |
- #' connector$get_server()(id = connector$get_dataname())+ #' |
||
75 | +110 |
- #' if (connector$is_failed()) {+ #' Create \link{CallableCode} object to execute specific code and get reproducible call. |
||
76 | +111 |
- #' break+ #' |
||
77 | +112 |
- #' }+ #' @param code (\code{character})\cr |
||
78 | +113 |
- #' }+ #' a string containing R code to reproduce the desired object. Please be aware |
||
79 | +114 |
- #' }+ #' that objects assigned to temporary environment are locked which means |
||
80 | +115 |
- #' )+ #' that they can't be modified. |
||
81 | +116 |
- #' }+ #' |
||
82 | +117 |
- #' )+ #' @return \code{CallableCode} object |
||
83 | +118 |
- #' \dontrun{+ #' |
||
84 | +119 |
- #' load_datasets(rdc)+ #' @export |
||
85 | +120 |
- #' get_datasets(rdc)+ #' |
||
86 | +121 |
- #' }+ #' @examples |
||
87 | +122 |
- #'+ #' cf <- callable_code(code = "mtcars") |
||
88 | +123 |
- #' # TealData --------+ #' cf$run() |
||
89 | +124 |
- #' \dontrun{+ #' cf$get_call() |
||
90 | +125 |
- #' drc <- cdisc_data(rdc, adae)+ callable_code <- function(code) {+ |
+ ||
126 | +19x | +
+ CallableCode$new(code) |
||
91 | +127 |
- #' get_datasets(drc)+ } |
92 | +1 |
- #' }+ #' Set arguments of a `CallableFunction` |
||
93 | +2 |
- get_datasets.TealDataAbstract <- function(x) { # nolint+ #' |
||
94 | -7x | +|||
3 | +
- res <- x$get_datasets()+ #' @description `r lifecycle::badge("stable")` |
|||
95 | -6x | +|||
4 | +
- if (length(res) == 0) {+ #' Set arguments of a `CallableFunction` |
|||
96 | -! | +|||
5 | +
- return(invisible(NULL))+ #' |
|||
97 | +6 |
- }+ #' @param x `CallableFunction` or `TealDatasetConnector`) |
||
98 | -6x | +|||
7 | +
- res+ #' @param args (`NULL` or named `list`) dynamic arguments to function |
|||
99 | +8 |
- }+ #' |
||
100 | +9 |
-
+ #' @return nothing |
||
101 | +10 |
- #' @rdname get_datasets+ #' @rdname set_args |
||
102 | +11 |
#' @export |
||
103 | +12 |
- #' @examples+ set_args <- function(x, args) { |
||
104 | -+ | |||
13 | +14x |
- #'+ UseMethod("set_args") |
||
105 | +14 |
- #' # TealDatasetConnector --------+ } |
||
106 | +15 |
- #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>%+ |
||
107 | +16 |
- #' set_args(list(dataname = "ADSL"))+ #' @rdname set_args |
||
108 | +17 |
- #' rdc <- cdisc_dataset_connector("ADSL", adsl_cf, keys = get_cdisc_keys("ADSL"))+ #' @export |
||
109 | +18 |
- #' \dontrun{+ #' @examples |
||
110 | +19 |
- #' load_datasets(rdc)+ #' ## Using CallableFunction |
||
111 | +20 |
- #' get_datasets(rdc)+ #' fun <- callable_function(example_cdisc_data) |
||
112 | +21 |
- #' }+ #' set_args(fun, list(dataname = "ADSL")) |
||
113 | +22 |
- get_datasets.TealDatasetConnector <- function(x) { # nolint+ set_args.CallableFunction <- function(x, args) { |
||
114 | -1x | +23 | +13x |
- res <- x$get_dataset()+ x$set_args(args) |
115 | -1x | +24 | +13x |
- if (length(res) == 0) {+ return(invisible(x)) |
116 | -! | +|||
25 | +
- return(invisible(NULL))+ } |
|||
117 | +26 |
- }+ |
||
118 | -1x | +|||
27 | +
- res+ #' @rdname set_args |
|||
119 | +28 |
- }+ #' @export |
||
120 | +29 |
-
+ #' @examples |
||
121 | +30 |
- #' @rdname get_datasets+ #' ## Using CallableCode |
||
122 | +31 |
- #' @export+ #' code <- callable_code("example_cdisc_data()") |
||
123 | +32 |
- #' @examples+ #' set_args(code, list(df = "adsl")) |
||
124 | +33 |
- #'+ set_args.CallableCode <- function(x, args) { |
||
125 | -+ | |||
34 | +! |
- #' # TealDataset --------+ warning( |
||
126 | -+ | |||
35 | +! |
- #' adsl <- cdisc_dataset(+ "'CallableCode' is unchangable. Ignoring arguments set by 'set_args'", |
||
127 | -+ | |||
36 | +! |
- #' dataname = "ADSL",+ call. = FALSE |
||
128 | +37 |
- #' x = teal.data::example_cdisc_data("ADSL"),+ ) |
||
129 | -+ | |||
38 | +! |
- #' code = "library(teal.data)\nADSL <- example_cdisc_data(\"ADSL\")"+ return(invisible(x)) |
||
130 | +39 |
- #' )+ } |
||
131 | +40 |
- #'+ |
||
132 | +41 |
- #' get_datasets(adsl)+ #' @rdname set_args |
||
133 | +42 |
- get_datasets.TealDataset <- function(x) {+ #' @export |
||
134 | -1x | +|||
43 | +
- x+ #' @examples |
|||
135 | +44 |
- }+ #' ## Using TealDatasetConnector |
||
136 | +45 |
-
+ #' ds <- dataset_connector("x", pull_callable = callable_function(data.frame)) |
||
137 | +46 |
- #' @rdname get_datasets+ #' set_args(ds, list(x = 1:5, y = letters[1:5])) |
||
138 | +47 |
- #' @export+ set_args.TealDatasetConnector <- function(x, args) { |
||
139 | -+ | |||
48 | +1x |
- get_datasets.teal_data <- function(x) {+ x$set_args(args) |
||
140 | -! | +|||
49 | +1x |
- as.list(x@env)[teal.data::get_dataname(x)]+ return(invisible(x)) |
||
141 | +50 |
}@@ -62514,14 +62565,14 @@ teal.data coverage - 74.87% |
1 |
- ## MAETealDataset ====+ #' Get a [`TealDataset`] objects. |
||
3 |
- #' @title R6 Class representing a `MultiAssayExperiment` object with its attributes+ #' @description `r lifecycle::badge("stable")` |
||
5 |
- #' @description `r lifecycle::badge("experimental")`+ #' @param x ([`TealData`])\cr |
||
6 |
- #' Any `MultiAssayExperiment` object can be stored inside this `MAETealDataset`.+ #' object containing datasets. |
||
7 |
- #' Some attributes like colnames, dimension or column names for a specific type will+ #' @export |
||
8 |
- #' be automatically derived.+ #' @return `list` or `TealDataset` objects |
||
9 |
- #'+ get_datasets <- function(x) { |
||
10 | -+ | 9x |
- #'+ UseMethod("get_datasets") |
11 |
- #' @param dataname (`character`)\cr+ } |
||
12 |
- #' A given name for the dataset it may not contain spaces+ |
||
13 |
- #' @param x (`MultiAssayExperiment`)\cr+ #' @rdname get_datasets |
||
14 |
- #' @param keys optional, (`character`)\cr+ #' @export |
||
15 |
- #' A vector of primary keys+ #' @examples |
||
16 |
- #' @param code (`character` or `CodeClass`)\cr+ #' |
||
17 |
- #' A character string defining the code needed to produce the data set in `x`.+ #' library(magrittr) |
||
18 |
- #' `initialize()` and `recreate()` accept code as `CodeClass`+ #' |
||
19 |
- #' which is also needed to preserve the code uniqueness and correct order.+ #' # TealData -------- |
||
20 |
- #' @param label (`character`)\cr+ #' adsl <- cdisc_dataset( |
||
21 |
- #' Label to describe the dataset+ #' dataname = "ADSL", |
||
22 |
- #' @param vars (named `list`)) \cr+ #' x = teal.data::example_cdisc_data("ADSL"), , |
||
23 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
||
24 |
- #' other constant value, this/these object(s) should be included as named+ #' ) |
||
25 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ #' |
||
26 |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ #' adae <- cdisc_dataset( |
||
27 |
- #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to+ #' dataname = "ADAE", |
||
28 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ #' x = teal.data::example_cdisc_data("ADAE"), |
||
29 |
- #' are included to this object as local `vars` and they cannot be modified+ #' code = "library(teal.data)\nADAE <- teal.data::example_cdisc_data(\"ADAE\")" |
||
30 |
- #' within another dataset.+ #' ) |
||
31 |
- #' @param metadata (named `list` or `NULL`) \cr+ #' |
||
32 |
- #' Field containing metadata about the dataset. Each element of the list+ #' rd <- cdisc_data(adsl, adae) |
||
33 |
- #' should be atomic and of length one.+ #' get_datasets(rd) |
||
35 |
- #' @seealso [`TealDataset`]+ #' # TealDataConnector -------- |
||
36 |
- #'+ #' random_data_connector <- function(dataname) { |
||
37 |
- MAETealDataset <- R6::R6Class( # nolint+ #' fun_dataset_connector( |
||
38 |
- "MAETealDataset",+ #' dataname = dataname, |
||
39 |
- inherit = TealDataset,+ #' fun = teal.data::example_cdisc_data, |
||
40 |
- ## __Public Methods ====+ #' fun_args = list(dataname = dataname), |
||
41 |
- public = list(+ #' ) |
||
42 |
- #' @description+ #' } |
||
43 |
- #' Create a new object of `MAETealDataset` class+ #' |
||
44 |
- #'+ #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>% |
||
45 |
- initialize = function(dataname,+ #' set_args(list(dataname = "ADSL")) |
||
46 |
- x,+ #' adsl <- cdisc_dataset_connector( |
||
47 |
- keys = character(0),+ #' dataname = "ADSL", |
||
48 |
- code = character(0),+ #' pull_callable = adsl_cf, |
||
49 |
- label = character(0),+ #' keys = get_cdisc_keys("ADSL") |
||
50 |
- vars = list(),+ #' ) |
||
51 |
- metadata = NULL) {+ #' adlb_cf <- callable_function(teal.data::example_cdisc_data) %>% |
||
52 | -18x | +
- if (!requireNamespace("MultiAssayExperiment", quietly = TRUE)) {+ #' set_args(list(dataname = "ADLB")) |
|
53 | -! | +
- stop("Cannot load MultiAssayExperiment - please install the package or restart your session.")+ #' adlb <- cdisc_dataset_connector( |
|
54 |
- }+ #' dataname = "ADLB", |
||
55 | -18x | +
- checkmate::assert_string(dataname)+ #' pull_callable = adlb_cf, |
|
56 | -18x | +
- stopifnot(inherits(x, "MultiAssayExperiment"))+ #' keys = get_cdisc_keys("ADLB") |
|
57 | -18x | +
- checkmate::assert_character(keys, any.missing = FALSE)+ #' ) |
|
58 | -18x | +
- checkmate::assert(+ #' |
|
59 | -18x | +
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ #' rdc <- relational_data_connector( |
|
60 | -18x | +
- checkmate::check_class(code, "CodeClass")+ #' connection = data_connection(), |
|
61 |
- )+ #' connectors = list(adsl, adlb) |
||
62 | -18x | +
- checkmate::assert_character(label, max.len = 1, null.ok = TRUE, any.missing = FALSE)+ #' ) |
|
63 | -18x | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' |
|
64 |
-
+ #' rdc$set_ui(function(id, connection, connectors) p("Example UI")) |
||
65 |
- # validate metadata as a list of length one atomic+ #' rdc$set_server( |
||
66 | -18x | +
- validate_metadata(metadata)+ #' function(id, connection, connectors) { |
|
67 |
-
+ #' moduleServer( |
||
68 | -18x | +
- private$.raw_data <- x+ #' id = id, |
|
69 | -18x | +
- private$metadata <- metadata+ #' module = function(input, output, session) { |
|
70 | -18x | +
- private$set_dataname(dataname)+ #' # Note this is simplified as we are not opening a real connection here |
|
71 | -18x | +
- self$set_vars(vars)+ #' for (connector in connectors) { |
|
72 | -18x | +
- self$set_dataset_label(label)+ #' set_args(connector, args = list(name = input$name)) |
|
73 | -18x | +
- self$set_keys(keys)+ #' # pull each dataset |
|
74 |
-
+ #' connector$get_server()(id = connector$get_dataname()) |
||
75 |
- # needed if recreating dataset - we need to preserve code order and uniqueness+ #' if (connector$is_failed()) { |
||
76 | -18x | +
- private$code <- CodeClass$new()+ #' break |
|
77 | -18x | +
- if (is.character(code)) {+ #' } |
|
78 | -17x | +
- self$set_code(code)+ #' } |
|
79 |
- } else {+ #' } |
||
80 | -1x | +
- private$code$append(code)+ #' ) |
|
81 |
- }+ #' } |
||
82 |
-
+ #' ) |
||
83 | -18x | +
- logger::log_trace("MAETealDataset$initialize initialized dataset: { deparse1(self$get_dataname()) }.")+ #' \dontrun{ |
|
84 |
-
+ #' load_datasets(rdc) |
||
85 | -18x | +
- return(invisible(self))+ #' get_datasets(rdc) |
|
86 |
- },+ #' } |
||
87 |
- # ___ check ====+ #' |
||
88 |
- #' @description+ #' # TealData -------- |
||
89 |
- #' Check to determine if the raw data is reproducible from the `get_code()` code.+ #' \dontrun{ |
||
90 |
- #' @return+ #' drc <- cdisc_data(rdc, adae) |
||
91 |
- #' `TRUE` if the dataset generated from evaluating the+ #' get_datasets(drc) |
||
92 |
- #' `get_code()` code is identical to the raw data, else `FALSE`.+ #' } |
||
93 |
- check = function() {+ get_datasets.TealDataAbstract <- function(x) { # nolint |
||
94 | -3x | +7x |
- logger::log_trace(+ res <- x$get_datasets() |
95 | -3x | +6x |
- "TealDataset$check executing the code to reproduce dataset: { deparse1(self$get_dataname()) }..."+ if (length(res) == 0) { |
96 | -+ | ! |
- )+ return(invisible(NULL)) |
97 | -3x | +
- if (!checkmate::test_character(self$get_code(), len = 1, pattern = "\\w+")) {+ } |
|
98 | -1x | +6x |
- stop(+ res |
99 | -1x | +
- sprintf(+ } |
|
100 | -1x | +
- "Cannot check preprocessing code of '%s' - code is empty.",+ |
|
101 | -1x | +
- self$get_dataname()+ #' @rdname get_datasets |
|
102 |
- )+ #' @export |
||
103 |
- )+ #' @examples |
||
104 |
- }+ #' |
||
105 |
-
+ #' # TealDatasetConnector -------- |
||
106 | -2x | +
- new_set <- private$execute_code(+ #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>% |
|
107 | -2x | +
- code = self$get_code_class(),+ #' set_args(list(dataname = "ADSL")) |
|
108 | -2x | +
- vars = private$vars+ #' rdc <- cdisc_dataset_connector("ADSL", adsl_cf, keys = get_cdisc_keys("ADSL")) |
|
109 |
- )+ #' \dontrun{ |
||
110 | -2x | +
- res_check <- tryCatch(+ #' load_datasets(rdc) |
|
111 |
- {+ #' get_datasets(rdc) |
||
112 | -2x | +
- identical(self$get_raw_data(), new_set)+ #' } |
|
113 |
- },+ get_datasets.TealDatasetConnector <- function(x) { # nolint |
||
114 | -2x | +1x |
- error = function(e) {+ res <- x$get_dataset() |
115 | -! | +1x |
- FALSE+ if (length(res) == 0) { |
116 | -+ | ! |
- }+ return(invisible(NULL)) |
117 |
- )+ } |
||
118 | -2x | +1x |
- logger::log_trace("TealDataset$check { deparse1(self$get_dataname()) } reproducibility result: { res_check }.")+ res |
119 |
-
+ } |
||
120 | -2x | +
- return(res_check)+ |
|
121 |
- },+ #' @rdname get_datasets |
||
122 |
- #' @description+ #' @export |
||
123 |
- #' Check if keys has been specified correctly for dataset. Set of `keys`+ #' @examples |
||
124 |
- #' should distinguish unique rows or be `character(0)`.+ #' |
||
125 |
- #'+ #' # TealDataset -------- |
||
126 |
- #' @return `TRUE` if dataset has been already pulled, else `FALSE`+ #' adsl <- cdisc_dataset( |
||
127 |
- check_keys = function(keys = private$.keys) {+ #' dataname = "ADSL", |
||
128 | -8x | +
- if (length(keys) > 0) {+ #' x = teal.data::example_cdisc_data("ADSL"), |
|
129 | -3x | +
- if (!all(keys %in% self$get_colnames())) {+ #' code = "library(teal.data)\nADSL <- example_cdisc_data(\"ADSL\")" |
|
130 | -1x | +
- stop("Primary keys specifed for ", self$get_dataname(), " do not exist in the data.")+ #' ) |
|
131 |
- }+ #' |
||
132 |
-
+ #' get_datasets(adsl) |
||
133 | -2x | +
- duplicates <- get_key_duplicates(as.data.frame(SummarizedExperiment::colData(self$get_raw_data())), keys)+ get_datasets.TealDataset <- function(x) { |
|
134 | -2x | +1x |
- if (nrow(duplicates) > 0) {+ x |
135 | -1x | +
- stop(+ } |
|
136 | -1x | +
- "Duplicate primary key values found in the dataset '", self$get_dataname(), "' :\n",+ |
|
137 | -1x | +
- paste0(utils::capture.output(print(duplicates))[-c(1, 3)], collapse = "\n"),+ #' @rdname get_datasets |
|
138 | -1x | +
- call. = FALSE+ #' @export |
|
139 |
- )+ get_datasets.teal_data <- function(x) { |
||
140 | -+ | ! |
- }+ as.list(x@env)[teal.data::get_dataname(x)] |
141 |
- }+ } |
142 | +1 |
- },+ #' This function returns a dummy dataset for testing examples and should only be used within `teal.data`. |
|
143 | +2 |
- #' @description+ #' |
|
144 | +3 |
- #' Derive the column names+ #' It is not meant to retrieve the SCDA dataset, and the dataset itself is not maintained here. |
|
145 | +4 |
- #' @return `character` vector.+ #' |
|
146 | +5 |
- get_colnames = function() {+ #' This function creates a copy of the SCDA data for testing purposes. |
|
147 | -8x | +||
6 | +
- colnames(SummarizedExperiment::colData(private$.raw_data))+ #' |
||
148 | +7 |
- },+ #' CDISC data includes `ADSL`, `ADAE`, `ADLB`, `ADCM`, `ADEX`, `ADRS`, `ADTR` and `ADTTE`. |
|
149 | +8 |
- #' @description+ #' |
|
150 | +9 |
- #' Derive the column labels+ #' @param dataname name of the `CDISC` dataset |
|
151 | +10 |
- #' @return `character` vector.+ #' |
|
152 | +11 |
- get_column_labels = function() {+ #' @return `cdisc_data` |
|
153 | -! | +||
12 | +
- vapply(+ #' |
||
154 | -! | +||
13 | +
- X = SummarizedExperiment::colData(private$.raw_data),+ #' @export |
||
155 | -! | +||
14 | +
- FUN.VALUE = character(1),+ example_cdisc_data <- function(dataname) { |
||
156 | -! | +||
15 | +
- FUN = function(x) {+ # Define the available datasets |
||
157 | -! | +||
16 | +33x |
- label <- attr(x, "label")+ datasets <- c("ADSL", "ADAE", "ADLB", "ADCM", "ADEX", "ADRS", "ADTR", "ADTTE", "ADVS") |
|
158 | -! | +||
17 | +
- if (length(label) != 1) {+ |
||
159 | -! | +||
18 | +
- NA_character_+ # Check if the provided dataname is valid+ |
+ ||
19 | +33x | +
+ if (dataname %in% datasets) {+ |
+ |
20 | +33x | +
+ dataset <- get(paste0("r", dataname))+ |
+ |
21 | +33x | +
+ return(dataset) |
|
160 | +22 |
- } else {+ } else { |
|
161 | +23 | ! |
- label+ stop("Invalid dataname. Please provide one of the following: ", paste(datasets, collapse = ", ")) |
162 | +24 |
- }+ } |
|
163 | +25 |
- }+ } |
164 | +1 |
- )+ #' Get dataset primary keys |
|
165 | +2 |
- },+ #' |
|
166 | +3 |
- #' @description+ #' @description `r lifecycle::badge("stable")` |
|
167 | +4 |
- #' Get the number of columns of the data+ #' Get dataset primary keys |
|
168 | +5 |
- #' @return `numeric` vector+ #' |
|
169 | +6 |
- get_ncol = function() {+ #' @param x an object of `TealDataset` or `TealDatasetConnector` class |
|
170 | -! | +||
7 | +
- ncol(SummarizedExperiment::colData(private$.raw_data))+ #' @param dataname (`character`) name of dataset to return keys for |
||
171 | +8 |
- },+ #' @param ... not used, only for support of S3 |
|
172 | +9 |
- #' @description+ #' |
|
173 | +10 |
- #' Get the number of rows of the data+ #' @return (`character`) vector of column names |
|
174 | +11 |
- #' @return `numeric` vector+ #' |
|
175 | +12 |
- get_nrow = function() {+ #' @export |
|
176 | -! | +||
13 | +
- nrow(SummarizedExperiment::colData(private$.raw_data))+ get_keys <- function(x, ...) {+ |
+ ||
14 | +92x | +
+ UseMethod("get_keys") |
|
177 | +15 |
- },+ } |
|
178 | +16 |
- #' @description+ |
|
179 | +17 |
- #' Derive the row names+ #' @rdname get_keys |
|
180 | +18 |
- #' @return `character` vector.+ #' @export |
|
181 | +19 |
- get_rownames = function() {+ #' @examples |
|
182 | -! | +||
20 | +
- rownames(SummarizedExperiment::colData(private$.raw_data))+ #' # TealDataset -------- |
||
183 | +21 |
- },+ #' |
|
184 | +22 |
- #' @description+ #' get_keys( |
|
185 | +23 |
- #' Prints this `MAETealDataset`.+ #' dataset( |
|
186 | +24 |
- #' @param ... additional arguments to the printing method+ #' "ADSL", |
|
187 | +25 |
- #'+ #' teal.data::example_cdisc_data("ADSL"), |
|
188 | +26 |
- #' @return invisibly self+ #' keys = get_cdisc_keys("ADSL"), |
|
189 | +27 |
- print = function(...) {+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
|
190 | -! | +||
28 | +
- cat(sprintf("A MAETealDataset object containing data of %d subjects.\n", self$get_nrow()))+ #' ) |
||
191 | -! | +||
29 | +
- print(MultiAssayExperiment::experiments(self$get_raw_data()))+ #' ) |
||
192 | -! | +||
30 | +
- invisible(self)+ get_keys.TealDataset <- function(x, ...) {+ |
+ ||
31 | +72x | +
+ check_ellipsis(...)+ |
+ |
32 | +72x | +
+ x$get_keys() |
|
193 | +33 |
- }+ } |
|
194 | +34 |
- ),+ |
|
195 | +35 |
- ## __Private Fields ====+ #' @rdname get_keys |
|
196 | +36 |
- private = list(+ #' @export |
|
197 | +37 |
- .raw_data = NULL,+ #' @examples |
|
198 | +38 |
- get_class_colnames = function(class_type = "character") {+ #' # TealDatasetConnector -------- |
|
199 | -! | +||
39 | +
- checkmate::assert_string(class_type)+ #' library(magrittr) |
||
200 | +40 |
-
+ #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>% |
|
201 | -! | +||
41 | +
- return_cols <- private$.colnames[which(vapply(+ #' set_args(list(dataname = "ADAE"))+ |
+ ||
42 | ++ |
+ #' get_keys(+ |
+ |
43 | ++ |
+ #' dataset_connector( |
|
202 | -! | +||
44 | +
- lapply(SummarizedExperiment::colData(private$.raw_data), class),+ #' "ADSL", |
||
203 | -! | +||
45 | +
- function(x, target_class_name) any(x %in% target_class_name),+ #' pull_fun_adsl, |
||
204 | -! | +||
46 | +
- logical(1),+ #' keys = get_cdisc_keys("ADSL"), |
||
205 | -! | +||
47 | +
- target_class_name = class_type+ #' ) |
||
206 | +48 |
- ))]+ #' ) |
|
207 | +49 |
-
+ get_keys.TealDatasetConnector <- function(x, ...) { |
|
208 | -! | +||
50 | +20x |
- return(return_cols)+ check_ellipsis(...)+ |
+ |
51 | +20x | +
+ x$get_keys() |
|
209 | +52 |
- },+ } |
|
210 | +53 | ||
211 | +54 |
- # Evaluate script code to modify data or to reproduce data+ #' @rdname get_keys |
|
212 | +55 |
- #+ #' @export |
|
213 | +56 |
- # @param code (`CodeClass`) the object storing the code to execute+ #' @examples |
|
214 | +57 |
- # @param vars (named `list`) additional pre-requisite vars to execute code+ #' # TealData -------- |
|
215 | +58 |
- # @return (`environment`) which stores modified `x`+ #' |
|
216 | +59 |
- execute_code = function(code, vars = list()) {+ #' get_keys( |
|
217 | -2x | +||
60 | +
- stopifnot(inherits(code, "CodeClass"))+ #' teal_data( |
||
218 | -2x | +||
61 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' dataset("x", data.frame(x1 = 1:10, y1 = 11:20), keys = "x1"), |
||
219 | +62 |
-
+ #' dataset("y", data.frame(x2 = 1:10, y2 = 11:20), keys = "x2") |
|
220 | -2x | +||
63 | +
- execution_environment <- new.env(parent = parent.env(globalenv()))+ #' ), |
||
221 | +64 |
-
+ #' "x" |
|
222 | +65 |
- # set up environment for execution+ #' ) |
|
223 | -2x | +||
66 | +
- for (vars_idx in seq_along(vars)) {+ get_keys.TealDataAbstract <- function(x, dataname, ...) { |
||
224 | +67 | ! |
- var_name <- names(vars)[[vars_idx]]+ check_ellipsis(...) |
225 | +68 | ! |
- var_value <- vars[[vars_idx]]+ get_keys(x$get_items(dataname)) |
226 | -! | +||
69 | +
- if (inherits(var_value, "TealDatasetConnector") || inherits(var_value, "TealDataset")) {+ } |
||
227 | -! | +||
70 | +
- var_value <- get_raw_data(var_value)+ |
||
228 | +71 |
- }+ |
|
229 | -! | +||
72 | +
- assign(envir = execution_environment, x = var_name, value = var_value)+ |
||
230 | +73 |
- }+ #' Set dataset primary keys |
|
231 | +74 |
-
+ #' |
|
232 | +75 |
- # execute+ #' @description `r lifecycle::badge("stable")` |
|
233 | -2x | +||
76 | +
- code$eval(envir = execution_environment)+ #' Set dataset primary keys |
||
234 | +77 |
-
+ #' |
|
235 | -2x | +||
78 | +
- if (!inherits(execution_environment[[self$get_dataname()]], "MultiAssayExperiment")) {+ #' @param x an object of `TealDataset` or `TealDatasetConnector` class |
||
236 | -! | +||
79 | +
- out_msg <- sprintf(+ #' @param keys optional, (`character`) vector with primary keys |
||
237 | -! | +||
80 | +
- "\n%s\n\n - Code from %s needs to return a MultiAssayExperiment assigned to an object of dataset name.",+ #' @param dataname (`character`) name of dataset for which set the keys |
||
238 | -! | +||
81 | +
- self$get_code(),+ #' @param ... not used, only for support of S3 |
||
239 | -! | +||
82 | +
- self$get_dataname()+ #' |
||
240 | +83 |
- )+ #' @return (`character`) vector of column names |
|
241 | +84 |
-
+ #' |
|
242 | -! | +||
85 | +
- rlang::with_options(+ #' @export |
||
243 | -! | +||
86 | +
- .expr = stop(out_msg, call. = FALSE),+ set_keys <- function(x, ...) { |
||
244 | -! | +||
87 | +119x |
- warning.length = max(min(8170, nchar(out_msg) + 30), 100)+ UseMethod("set_keys") |
|
245 | +88 |
- )+ } |
|
246 | +89 |
- }+ |
|
247 | +90 |
-
+ #' @rdname set_keys |
|
248 | -2x | +||
91 | +
- new_set <- execution_environment[[self$get_dataname()]]+ #' @export |
||
249 | +92 |
-
+ #' @examples |
|
250 | -2x | +||
93 | +
- return(new_set)+ #' # TealDataset -------- |
||
251 | +94 |
- }+ #' |
|
252 | +95 |
- )+ #' set_keys( |
|
253 | +96 |
- )+ #' dataset( |
|
254 | +97 |
-
+ #' "DF", |
|
255 | +98 |
- #' S3 method to construct an `MAETealDataset` object from `MultiAssayExperiment`+ #' data.frame(ID = 1:10, x = runif(10)) |
|
256 | +99 |
- #'+ #' ), |
|
257 | +100 |
- #' @rdname dataset+ #' keys = c("ID") |
|
258 | +101 |
- #'+ #' ) |
|
259 | +102 |
- #' @examples+ set_keys.TealDataset <- function(x, keys, ...) { |
|
260 | -+ | ||
103 | +119x |
- #' # Simple example+ check_ellipsis(...)+ |
+ |
104 | +119x | +
+ x$set_keys(keys) |
|
261 | +105 |
- #' utils::data(miniACC, package = "MultiAssayExperiment")+ } |
|
262 | +106 |
- #' mae_d <- dataset(+ |
|
263 | +107 |
- #' "MAE",+ #' @rdname set_keys |
|
264 | +108 |
- #' miniACC,+ #' @export |
|
265 | +109 |
- #' keys = c("STUDYID", "USUBJID"),+ #' @examples |
|
266 | +110 |
- #' metadata = list(type = "example")+ #' # TealDatasetConnector -------- |
|
267 | +111 |
- #' )+ #' |
|
268 | +112 |
- #' mae_d$get_dataname()+ #' pull_fun <- callable_function( |
|
269 | +113 |
- #' mae_d$get_dataset_label()+ #' function() { |
|
270 | +114 |
- #' mae_d$get_metadata()+ #' data.frame(ID = 1:10, x = runif(10)) |
|
271 | +115 |
- #' mae_d$get_code()+ #' } |
|
272 | +116 |
- #' mae_d$get_raw_data()+ #' ) |
|
273 | +117 |
- #' @export+ #' set_keys( |
|
274 | +118 |
- dataset.MultiAssayExperiment <- function(dataname, # nolint+ #' dataset_connector( |
|
275 | +119 |
- x,+ #' "DF", |
|
276 | +120 |
- keys = character(0),+ #' pull_fun |
|
277 | +121 |
- label = data_label(x),+ #' ), |
|
278 | +122 |
- code = character(0),+ #' keys = c("ID") |
|
279 | +123 |
- vars = list(),+ #' ) |
|
280 | +124 |
- metadata = NULL) {+ set_keys.TealDatasetConnector <- function(x, keys, ...) { |
|
281 | -4x | +||
125 | +! |
- if (!requireNamespace("MultiAssayExperiment", quietly = TRUE)) {+ check_ellipsis(...) |
|
282 | +126 | ! |
- stop("Cannot load MultiAssayExperiment - please install the package or restart your session.")+ x$set_keys(keys) |
283 | +127 |
- }+ } |
|
284 | -4x | +||
128 | +
- checkmate::assert_string(dataname)+ |
||
285 | -4x | +||
129 | +
- checkmate::assert(+ #' @rdname set_keys |
||
286 | -4x | +||
130 | +
- checkmate::check_character(code, max.len = 1, any.missing = FALSE),+ #' @export |
||
287 | -4x | +||
131 | +
- checkmate::check_class(code, "CodeClass")+ #' @examples |
||
288 | +132 |
- )+ #' # TealData -------- |
|
289 | -4x | +||
133 | +
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #'+ |
+ ||
134 | ++ |
+ #' set_keys(+ |
+ |
135 | ++ |
+ #' teal_data(+ |
+ |
136 | ++ |
+ #' dataset("x", data.frame(x1 = 1:10, y1 = 11:20), keys = "x1"),+ |
+ |
137 | ++ |
+ #' dataset("y", data.frame(x2 = 1:10, y2 = 11:20), keys = "x2") |
|
290 | +138 |
-
+ #' ), |
|
291 | -4x | +||
139 | +
- MAETealDataset$new(+ #' "x", |
||
292 | -4x | +||
140 | +
- dataname = dataname,+ #' c("x1", "y1") |
||
293 | -4x | +||
141 | +
- x = x,+ #' ) |
||
294 | -4x | +||
142 | +
- keys = keys,+ set_keys.TealDataAbstract <- function(x, dataname, keys, ...) { |
||
295 | -4x | +||
143 | +! |
- code = code,+ check_ellipsis(...) |
|
296 | -4x | +||
144 | +! |
- label = label,+ set_keys(x$get_items(dataname), keys = keys) |
|
297 | -4x | +||
145 | +! |
- vars = vars,+ return(invisible(x)) |
|
298 | -4x | +||
146 | +
- metadata = metadata+ } |
299 | +1 |
- )+ #' Teal data |
|
300 | +2 |
- }+ #' |
|
301 | +3 |
-
+ #' @description `r lifecycle::badge("stable")` |
|
302 | +4 |
- #' The constructor of `MAETealDataset`+ #' Universal function to pass data to teal application |
|
303 | +5 |
#' |
|
304 | +6 |
- #' @description `r lifecycle::badge("deprecated")`+ #' @param ... (`TealDataConnector`, `TealDataset`, `TealDatasetConnector`, `any`)\cr |
|
305 | +7 |
- #'+ #' - When one of the `Teal*` objects are provided, then function returns `TealData` object. |
|
306 | +8 |
- #' @inheritParams dataset+ #' This way of specifying data is deprecated and will be removed in the next release. |
|
307 | +9 |
- #' @param x (`MultiAssayExperiment`)+ #' - From version 0.4.0, one can provide any object as a named argument and function will |
|
308 | +10 |
- #'+ #' return `teal_data` object. Objects provided in `...` will be stored in `teal_data` environment |
|
309 | +11 |
- #' @examples+ #' under the same name as the argument name. |
|
310 | +12 |
- #' # Simple example+ #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr |
|
311 | +13 |
- #' utils::data(miniACC, package = "MultiAssayExperiment")+ #' (optional) object with dataset column relationships used for joining. |
|
312 | +14 |
- #' mae_d <- dataset("MAE", miniACC)+ #' If empty then no joins between pairs of objects |
|
313 | +15 |
- #' mae_d$get_dataname()+ #' @param code (`character`) code to reproduce the datasets. |
|
314 | +16 |
- #' mae_d$get_dataset_label()+ #' @param check (`logical`) reproducibility check - whether to perform a check that the pre-processing |
|
315 | +17 |
- #' mae_d$get_code()+ #' code included in the object definitions actually produces those objects. |
|
316 | +18 |
- #' mae_d$get_raw_data()+ #' If `check` is true and preprocessing code is empty an error will be thrown. |
|
317 | +19 |
- #' @export+ #' |
|
318 | +20 |
- mae_dataset <- function(dataname,+ #' @return (`TealData` or `teal_data`) object |
|
319 | +21 |
- x,+ #' |
|
320 | +22 |
- label = data_label(x),+ #' @export |
|
321 | +23 |
- code = character(0),+ #' |
|
322 | +24 |
- vars = list()) {+ #' @examples |
|
323 | -! | +||
25 | +
- lifecycle::deprecate_soft(+ #' |
||
324 | -! | +||
26 | +
- when = "0.10.1",+ #' teal_data( |
||
325 | -! | +||
27 | +
- what = "teal.data::mae_dataset()",+ #' x1 = iris, |
||
326 | -! | +||
28 | +
- with = "teal.data::dataset()"+ #' x2 = mtcars, |
||
327 | +29 |
- )+ #' code = quote({ |
|
328 | +30 |
-
+ #' x1 <- iris |
|
329 | -! | +||
31 | +
- if (!inherits(x, "MultiAssayExperiment")) {+ #' x2 <- mtcars |
||
330 | -! | +||
32 | +
- stop("Argument x must be a MultiAssayExperiment object")+ #' }) |
||
331 | +33 |
- }+ #' ) |
|
332 | +34 |
-
+ teal_data <- function(..., |
|
333 | -! | +||
35 | +
- dataset(+ join_keys = teal.data::join_keys(), |
||
334 | -! | +||
36 | +
- dataname = dataname,+ code = "", |
||
335 | -! | +||
37 | +
- x = x,+ check = FALSE) { |
||
336 | -! | +||
38 | +56x |
- code = code,+ data_objects <- list(...) |
|
337 | -! | +||
39 | +56x |
- label = label,+ if (inherits(join_keys, "JoinKeySet")) { |
|
338 | +40 | ! |
- vars = vars+ join_keys <- teal.data::join_keys(join_keys) |
339 | +41 |
- )+ } |
|
340 | +42 |
- }+ if ( |
1 | -+ | |||
43 | +56x |
- #' Include `JS` files from `/inst/js/` package directory to application header+ checkmate::test_list(data_objects, types = c("TealDataConnector", "TealDataset", "TealDatasetConnector")) |
||
2 | +44 |
- #'+ ) { |
||
3 | -+ | |||
45 | +46x |
- #' `system.file` should not be used to access files in other packages, it does+ lifecycle::deprecate_warn( |
||
4 | -+ | |||
46 | +46x |
- #' not work with `devtools`. Therefore, we redefine this method in each package+ when = "0.3.1", |
||
5 | -+ | |||
47 | +46x |
- #' as needed. Thus, we do not export this method+ "teal_data( |
||
6 | -+ | |||
48 | +46x |
- #'+ data_objects = 'should use data directly. Using TealDatasetConnector and TealDataset is deprecated. |
||
7 | -+ | |||
49 | +46x |
- #' @param pattern (`character`) pattern of files to be included, passed to `system.file`+ Find more information on https://github.com/insightsengineering/teal/discussions/945' |
||
8 | +50 |
- #' @param except (`character`) vector of basename filenames to be excluded+ )" |
||
9 | +51 |
- #'+ ) |
||
10 | -+ | |||
52 | +46x |
- #' @return HTML code that includes `JS` files+ update_join_keys_to_primary(data_objects, join_keys) |
||
11 | +53 |
- #' @keywords internal+ |
||
12 | -+ | |||
54 | +46x |
- include_js_files <- function(pattern = NULL, except = NULL) {+ x <- TealData$new(..., check = check, join_keys = join_keys) |
||
13 | -4x | +55 | +46x |
- checkmate::assert_character(except, min.len = 1, any.missing = FALSE, null.ok = TRUE)+ if (length(code) > 0 && !identical(code, "")) { |
14 | -4x | +56 | +2x |
- js_files <- list.files(+ x$set_pull_code(code = code) |
15 | -4x | +|||
57 | +
- system.file("js", package = "teal.data", mustWork = TRUE),+ } |
|||
16 | -4x | +58 | +45x |
- pattern = pattern, full.names = TRUE+ x$check_reproducibility() |
17 | -+ | |||
59 | +44x |
- )+ x$check_metadata() |
||
18 | -4x | +60 | +43x |
- js_files <- js_files[!(basename(js_files) %in% except)] # no-op if except is NULL+ x+ |
+
61 | ++ |
+ } else { |
||
19 | -4x | +62 | +10x |
- if (length(js_files) == 0) {+ if (!checkmate::test_names(names(data_objects), type = "named")) { |
20 | +63 | ! |
- return(NULL)+ stop("Dot (`...`) arguments on `teal_data()` must be named.") |
|
21 | +64 |
- }+ } |
||
22 | -4x | +65 | +10x |
- return(singleton(lapply(js_files, includeScript)))+ new_teal_data( |
23 | -+ | |||
66 | +10x |
- }+ data = data_objects, |
1 | -+ | |||
67 | +10x |
- #' Topological graph sort+ code = code,+ |
+ ||
68 | +10x | +
+ keys = join_keys |
||
2 | +69 |
- #'+ ) |
||
3 | +70 |
- #' Graph is a list which for each node contains a vector of child nodes+ } |
||
4 | +71 |
- #' in the returned list, parents appear before their children.+ } |
||
5 | +72 |
- #'+ |
||
6 | +73 |
- #' Implementation of `Kahn` algorithm with a modification to maintain the order of input elements.+ #' Load `TealData` object from a file |
||
7 | +74 |
#' |
||
8 | +75 |
- #' @param graph (named `list`) list with node vector elements+ #' @description `r lifecycle::badge("experimental")` |
||
9 | +76 |
- #' @keywords internal+ #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise. |
||
10 | +77 |
#' |
||
11 | +78 |
- #' @examples+ #' @param path A (`connection`) or a (`character`)\cr |
||
12 | +79 |
- #' teal.data:::topological_sort(list(A = c(), B = c("A"), C = c("B"), D = c("A")))+ #' string giving the pathname of the file or URL to read from. "" indicates the connection `stdin`. |
||
13 | +80 | ++ |
+ #' @param code (`character`)\cr+ |
+ |
81 |
- #' teal.data:::topological_sort(list(D = c("A"), A = c(), B = c("A"), C = c("B")))+ #' reproducible code to re-create object |
|||
14 | +82 |
- #' teal.data:::topological_sort(list(D = c("A"), B = c("A"), C = c("B"), A = c()))+ #' |
||
15 | +83 |
- topological_sort <- function(graph) {+ #' @return `TealData` object |
||
16 | +84 |
- # compute in-degrees+ #' |
||
17 | -29x | +|||
85 | +
- in_degrees <- list()+ #' |
|||
18 | -29x | +|||
86 | +
- for (node in names(graph)) {+ #' @export |
|||
19 | -55x | +|||
87 | +
- in_degrees[[node]] <- 0+ #' |
|||
20 | -55x | +|||
88 | +
- for (to_edge in graph[[node]]) {+ #' @examples |
|||
21 | -27x | +|||
89 | +
- in_degrees[[to_edge]] <- 0+ #' # simple example |
|||
22 | +90 |
- }+ #' file_example <- tempfile(fileext = ".R") |
||
23 | +91 |
- }+ #' writeLines( |
||
24 | +92 |
-
+ #' text = c( |
||
25 | -29x | +|||
93 | +
- for (node in graph) {+ #' "library(teal.data) |
|||
26 | -55x | +|||
94 | +
- for (to_edge in node) {+ #' |
|||
27 | -27x | +|||
95 | +
- in_degrees[[to_edge]] <- in_degrees[[to_edge]] + 1+ #' x1 <- dataset(dataname = \"IRIS\", |
|||
28 | +96 |
- }+ #' x = iris, |
||
29 | +97 |
- }+ #' code = \"IRIS <- iris\") |
||
30 | +98 |
-
+ #' |
||
31 | +99 |
- # sort+ #' x2 <- dataset(dataname = \"MTCARS\", |
||
32 | -29x | +|||
100 | +
- visited <- 0+ #' x = mtcars, |
|||
33 | -29x | +|||
101 | +
- sorted <- list()+ #' code = \"MTCARS <- mtcars\") |
|||
34 | -29x | +|||
102 | +
- zero_in <- list()+ #' |
|||
35 | -29x | +|||
103 | +
- for (node in names(in_degrees)) {+ #' teal_data(x1, x2)" |
|||
36 | -39x | +|||
104 | +
- if (in_degrees[[node]] == 0) zero_in <- append(zero_in, node)+ #' ), |
|||
37 | +105 |
- }+ #' con = file_example |
||
38 | -29x | +|||
106 | +
- zero_in <- rev(zero_in)+ #' ) |
|||
39 | +107 |
-
+ #' teal_data_file(file_example, code = character(0)) |
||
40 | -29x | +|||
108 | +
- while (length(zero_in) != 0) {+ teal_data_file <- function(path, code = get_code(path)) { |
|||
41 | -57x | +109 | +2x |
- visited <- visited + 1+ object <- object_file(path, "TealData") |
42 | -57x | +110 | +2x |
- sorted <- c(zero_in[[1]], sorted)+ object$mutate(code) |
43 | -57x | +111 | +2x |
- for (edge_to in graph[[zero_in[[1]]]]) {+ return(object) |
44 | -26x | +|||
112 | +
- in_degrees[[edge_to]] <- in_degrees[[edge_to]] - 1+ } |
|||
45 | -26x | +|||
113 | +
- if (in_degrees[[edge_to]] == 0) {+ |
|||
46 | -18x | +|||
114 | +
- zero_in <- append(zero_in, edge_to, 1)+ #' Add primary keys as join_keys to a dataset self |
|||
47 | +115 |
- }+ #' |
||
48 | +116 |
- }+ #' @param data_objects (`list`) of `TealDataset`, `TealDatasetConnector` or `TealDataConnector` objects |
||
49 | -57x | +|||
117 | +
- zero_in[[1]] <- NULL+ #' @param join_keys (`JoinKeys`) object |
|||
50 | +118 |
- }+ #' |
||
51 | +119 |
-
+ #' @keywords internal |
||
52 | -29x | +|||
120 | +
- if (visited != length(in_degrees)) {+ update_join_keys_to_primary <- function(data_objects, join_keys) { |
|||
53 | -1x | +121 | +79x |
- stop(+ lapply(data_objects, function(obj) { |
54 | -1x | +122 | +142x |
- "Graph is not a directed acyclic graph. Cycles involving nodes: ",+ if (inherits(obj, "TealDataConnector")) { |
55 | -1x | -
- paste0(setdiff(names(in_degrees), sorted), collapse = " ")- |
- ||
56 | -+ | 123 | +9x |
- )+ update_join_keys_to_primary(obj$get_items(), join_keys) |
57 | +124 |
- } else {+ } else { |
||
58 | -28x | +125 | +133x |
- return(sorted)+ dataname <- obj$get_dataname() |
59 | -+ | |||
126 | +133x |
- }+ if (length(join_keys$get(dataname, dataname)) == 0) { |
||
60 | -+ | |||
127 | +91x |
- }+ join_keys$mutate( |
||
61 | -+ | |||
128 | +91x |
-
+ dataname, |
||
62 | -+ | |||
129 | +91x |
- #' Checks whether a graph is a `Directed Acyclic Graph (DAG)`+ dataname, |
||
63 | -+ | |||
130 | +91x |
- #'+ obj$get_keys() |
||
64 | +131 |
- #' @inheritParams topological_sort+ ) |
||
65 | +132 |
- #' @return `logical(1)` `TRUE` if the graph is a `DAG`; `FALSE` otherwise+ } |
||
66 | +133 |
- #' @keywords internal+ } |
||
67 | +134 |
- is_dag <- function(graph) {- |
- ||
68 | -29x | -
- inherits(try(topological_sort(graph), silent = TRUE), "try-error")+ }) |
||
69 | +135 |
}@@ -65556,14 +65718,14 @@ teal.data coverage - 74.87% |
1 |
- #' Mutate dataset by code+ #' Topological graph sort |
||
3 |
- #' @description `r lifecycle::badge("experimental")`+ #' Graph is a list which for each node contains a vector of child nodes |
||
4 |
- #'+ #' in the returned list, parents appear before their children. |
||
5 |
- #' @param x (`TealDataset`)\cr+ #' |
||
6 |
- #' object.+ #' Implementation of `Kahn` algorithm with a modification to maintain the order of input elements. |
||
7 |
- #' @param dataname (`character`)\cr+ #' |
||
8 |
- #' `Dataname` to be mutated.+ #' @param graph (named `list`) list with node vector elements |
||
9 |
- #' @param code (`character`)\cr+ #' @keywords internal |
||
10 |
- #' Code to mutate the dataset. Must contain the `dataset$dataname`. Or can also be an object+ #' |
||
11 |
- #' of class `PythonCodeClass` returned by [`python_code`].+ #' @examples |
||
12 |
- #' @param script (`character`)\cr+ #' teal.data:::topological_sort(list(A = c(), B = c("A"), C = c("B"), D = c("A"))) |
||
13 |
- #' file that contains R Code that can be read using [`read_script`].+ #' teal.data:::topological_sort(list(D = c("A"), A = c(), B = c("A"), C = c("B"))) |
||
14 |
- #' Preferred before `code` argument.+ #' teal.data:::topological_sort(list(D = c("A"), B = c("A"), C = c("B"), A = c())) |
||
15 |
- #' @param vars (named `list`)) \cr+ topological_sort <- function(graph) { |
||
16 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ # compute in-degrees |
||
17 | -+ | 29x |
- #' other constant value, this/these object(s) should be included as named+ in_degrees <- list() |
18 | -+ | 29x |
- #' element(s) of the list. For example if this object code needs `ADSL`+ for (node in names(graph)) { |
19 | -+ | 55x |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ in_degrees[[node]] <- 0 |
20 | -+ | 55x |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ for (to_edge in graph[[node]]) { |
21 | -+ | 27x |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ in_degrees[[to_edge]] <- 0 |
22 |
- #' are included to this object as local `vars` and they cannot be modified+ } |
||
23 |
- #' within another dataset.+ } |
||
24 |
- #' @param ... not used, only for support of S3+ |
||
25 | -+ | 29x |
- #'+ for (node in graph) { |
26 | -+ | 55x |
- #' @return modified `x` object+ for (to_edge in node) { |
27 | -+ | 27x |
- #'+ in_degrees[[to_edge]] <- in_degrees[[to_edge]] + 1 |
28 |
- #' @export+ } |
||
29 |
- mutate_dataset <- function(x, ...) {+ } |
||
30 | -64x | +
- UseMethod("mutate_dataset")+ |
|
31 |
- }+ # sort |
||
32 | -+ | 29x |
-
+ visited <- 0 |
33 | -+ | 29x |
- #' @rdname mutate_dataset+ sorted <- list() |
34 | -+ | 29x |
- #' @examples+ zero_in <- list() |
35 | -+ | 29x |
- #' library(magrittr)+ for (node in names(in_degrees)) { |
36 | -+ | 39x |
- #'+ if (in_degrees[[node]] == 0) zero_in <- append(zero_in, node) |
37 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ } |
||
38 | -+ | 29x |
- #'+ zero_in <- rev(zero_in) |
39 |
- #' ADSL_dataset <- dataset(+ |
||
40 | -+ | 29x |
- #' dataname = "ADSL",+ while (length(zero_in) != 0) { |
41 | -+ | 57x |
- #' x = ADSL,+ visited <- visited + 1 |
42 | -+ | 57x |
- #' label = "AdAM subject-level dataset",+ sorted <- c(zero_in[[1]], sorted) |
43 | -+ | 57x |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ for (edge_to in graph[[zero_in[[1]]]]) { |
44 | -+ | 26x |
- #' )+ in_degrees[[edge_to]] <- in_degrees[[edge_to]] - 1 |
45 | -+ | 26x |
- #' ADSL_mutated <- ADSL_dataset %>%+ if (in_degrees[[edge_to]] == 0) { |
46 | -+ | 18x |
- #' mutate_dataset(code = "ADSL$new_variable <- 1")+ zero_in <- append(zero_in, edge_to, 1) |
47 |
- #'+ } |
||
48 |
- #' ADSL_mutated$get_raw_data()$new_variable[1]+ } |
||
49 | -+ | 57x |
- #'+ zero_in[[1]] <- NULL |
50 |
- #' # Use an R script to mutate the data+ } |
||
51 |
- #' file_example <- tempfile(fileext = ".R")+ |
||
52 | -+ | 29x |
- #' writeLines(+ if (visited != length(in_degrees)) { |
53 | -+ | 1x |
- #' text = c(+ stop( |
54 | -+ | 1x |
- #' "ADSL <- ADSL %>%+ "Graph is not a directed acyclic graph. Cycles involving nodes: ", |
55 | -+ | 1x |
- #' dplyr::mutate(new_variable = new_variable * 2)"+ paste0(setdiff(names(in_degrees), sorted), collapse = " ") |
56 |
- #' ),+ ) |
||
57 |
- #' con = file_example+ } else { |
||
58 | -+ | 28x |
- #' )+ return(sorted) |
59 |
- #'+ } |
||
60 |
- #' ADSL_mutated <- ADSL_mutated %>%+ } |
||
61 |
- #' mutate_dataset(script = file_example)+ |
||
62 |
- #'+ #' Checks whether a graph is a `Directed Acyclic Graph (DAG)` |
||
63 |
- #' ADSL_mutated$get_raw_data()$new_variable[1]+ #' |
||
64 |
- #'+ #' @inheritParams topological_sort |
||
65 |
- #' ADSL_mutated <- ADSL_mutated %>%+ #' @return `logical(1)` `TRUE` if the graph is a `DAG`; `FALSE` otherwise |
||
66 |
- #' mutate_dataset(code = read_script(file_example))+ #' @keywords internal |
||
67 |
- #'+ is_dag <- function(graph) { |
||
68 | -+ | 29x |
- #' ADSL_mutated$get_raw_data()$new_variable[1]+ inherits(try(topological_sort(graph), silent = TRUE), "try-error") |
69 |
- #' @export- |
- ||
70 | -- |
- mutate_dataset.TealDataset <- function(x,- |
- |
71 | -- |
- code = character(0),- |
- |
72 | -- |
- script = character(0),- |
- |
73 | -- |
- vars = list(),+ } |
74 | +1 |
- ...) {- |
- ||
75 | -31x | -
- check_ellipsis(...)- |
- ||
76 | -31x | -
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' Include `JS` files from `/inst/js/` package directory to application header |
||
77 | +2 | - - | -||
78 | -31x | -
- code <- code_from_script(code, script)- |
- ||
79 | -29x | -
- x$mutate(code = code, vars = vars, ...)+ #' |
||
80 | +3 |
- }+ #' `system.file` should not be used to access files in other packages, it does |
||
81 | +4 |
-
+ #' not work with `devtools`. Therefore, we redefine this method in each package |
||
82 | +5 |
-
+ #' as needed. Thus, we do not export this method |
||
83 | +6 |
- #' @rdname mutate_dataset+ #' |
||
84 | +7 |
- #' @export+ #' @param pattern (`character`) pattern of files to be included, passed to `system.file` |
||
85 | +8 |
- mutate_dataset.TealDatasetConnector <- function(x, # nolint+ #' @param except (`character`) vector of basename filenames to be excluded |
||
86 | +9 |
- code = character(0),+ #' |
||
87 | +10 |
- script = character(0),+ #' @return HTML code that includes `JS` files |
||
88 | +11 |
- vars = list(),+ #' @keywords internal |
||
89 | +12 |
- ...) {+ include_js_files <- function(pattern = NULL, except = NULL) { |
||
90 | -29x | +13 | +4x |
- check_ellipsis(...)+ checkmate::assert_character(except, min.len = 1, any.missing = FALSE, null.ok = TRUE) |
91 | -29x | +14 | +4x |
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ js_files <- list.files( |
92 | -29x | +15 | +4x |
- code <- code_from_script(code, script)+ system.file("js", package = "teal.data", mustWork = TRUE), |
93 | -29x | +16 | +4x |
- x$mutate(code = code, vars = vars, ...)+ pattern = pattern, full.names = TRUE |
94 | +17 |
- }+ ) |
||
95 | -+ | |||
18 | +4x |
-
+ js_files <- js_files[!(basename(js_files) %in% except)] # no-op if except is NULL |
||
96 | -+ | |||
19 | +4x |
-
+ if (length(js_files) == 0) { |
||
97 | -+ | |||
20 | +! |
- #' @rdname mutate_dataset+ return(NULL) |
||
98 | +21 |
- #' @export+ } |
||
99 | -+ | |||
22 | +4x |
- mutate_dataset.TealDataAbstract <- function(x,+ return(singleton(lapply(js_files, includeScript))) |
||
100 | +23 |
- dataname,+ } |
101 | +1 |
- code = character(0),+ #' Function to get join keys from a `` object |
||
102 | +2 |
- script = character(0),+ #' @param data `` - object to extract the join keys |
||
103 | +3 |
- vars = list(),+ #' @return Either `JoinKeys` object or `NULL` if no join keys |
||
104 | +4 |
- ...) {- |
- ||
105 | -4x | -
- check_ellipsis(...)- |
- ||
106 | -4x | -
- checkmate::assert_list(vars, min.len = 0, names = "unique")+ #' @export |
||
107 | +5 | - - | -||
108 | -4x | -
- code <- code_from_script(code, script)+ get_join_keys <- function(data) { |
||
109 | -4x | +6 | +14x |
- x$mutate_dataset(dataname = dataname, code = code, vars = vars)+ UseMethod("get_join_keys", data) |
110 | +7 |
} |
||
111 | -- | - - | -||
112 | +8 | |||
113 | +9 |
-
+ #' @rdname get_join_keys |
||
114 | +10 |
- #' Mutate data by code+ #' @export |
||
115 | +11 |
- #'+ get_join_keys.teal_data <- function(data) { |
||
116 | -+ | |||
12 | +5x |
- #' @description `r lifecycle::badge("experimental")`+ data@join_keys |
||
117 | +13 |
- #' Code used in this mutation is not linked to particular+ } |
||
118 | +14 |
- #' but refers to all datasets.+ |
||
119 | +15 |
- #' Consequence of this is that when using `get_code(<dataset>)` this+ #' @rdname get_join_keys |
||
120 | +16 |
- #' part of the code will be returned for each dataset specified. This method+ #' @export |
||
121 | +17 |
- #' should be used only if particular call involve changing multiple datasets.+ get_join_keys.JoinKeys <- function(data) { |
||
122 | -+ | |||
18 | +9x |
- #' Otherwise please use `mutate_dataset`.+ data |
||
123 | +19 |
- #' Execution of the code is delayed after datasets are pulled+ } |
||
124 | +20 |
- #' (`isTRUE(is_pulled)`).+ |
||
125 | +21 |
- #'+ #' @rdname get_join_keys |
||
126 | +22 |
- #' @param x (`TealDataAbstract`)\cr+ #' @inheritParams mutate_join_keys |
||
127 | +23 |
- #' object.+ #' @param value value to assign |
||
128 | +24 |
- #' @inheritParams mutate_dataset+ #' @export |
||
129 | +25 |
- #'+ `get_join_keys<-` <- function(data, dataset_1, dataset_2 = NULL, value) { |
||
130 | -+ | |||
26 | +2x |
- #' @return modified `x` object+ UseMethod("get_join_keys<-", data) |
||
131 | +27 |
- #'+ } |
||
132 | +28 |
- #' @export+ |
||
133 | +29 |
- mutate_data <- function(x,+ #' @rdname get_join_keys |
||
134 | +30 |
- code = character(0),+ #' @inheritParams mutate_join_keys |
||
135 | +31 |
- script = character(0),+ #' @export |
||
136 | +32 |
- vars = list()) {+ `get_join_keys<-.JoinKeys` <- function(data, dataset_1, dataset_2 = NULL, value) { |
||
137 | -3x | +33 | +1x |
- UseMethod("mutate_data")+ data |
138 | +34 |
} |
||
139 | +35 | |||
140 | +36 |
- #' @rdname mutate_data+ #' @rdname get_join_keys |
||
141 | +37 |
#' @export |
||
142 | -- |
- mutate_data.TealDataAbstract <- function(x,- |
- ||
143 | -- |
- code = character(0),- |
- ||
144 | -- |
- script = character(0),- |
- ||
145 | -- |
- vars = list()) {- |
- ||
146 | -3x | -
- checkmate::assert_list(vars, min.len = 0, names = "unique")- |
- ||
147 | +38 | - - | -||
148 | -3x | -
- code <- code_from_script(code, script)- |
- ||
149 | -3x | -
- x$mutate(code = code, vars = vars)+ `get_join_keys<-.teal_data` <- function(data, dataset_1, dataset_2 = NULL, value) { |
||
150 | -3x | +39 | +1x |
- return(invisible(x))+ data |
151 | +40 |
}@@ -67290,14 +67331,14 @@ teal.data coverage - 74.87% |
1 |
- ## CDISCTealDatasetConnector ====+ #' Mutate dataset by code |
|||
3 |
- #' @title A `CDISCTealDatasetConnector` class of objects+ #' @description `r lifecycle::badge("experimental")` |
|||
5 |
- #' @description `r lifecycle::badge("stable")`+ #' @param x (`TealDataset`)\cr |
|||
6 |
- #' Objects of this class store the connection function to fetch a single dataset.+ #' object. |
|||
7 |
- #'+ #' @param dataname (`character`)\cr |
|||
8 |
- #' The difference compared to `TealDatasetConnector` is a parent field that+ #' `Dataname` to be mutated. |
|||
9 |
- #' indicates name of the parent dataset. Note that the parent field might+ #' @param code (`character`)\cr |
|||
10 |
- #' be empty (i.e. `character(0)`).+ #' Code to mutate the dataset. Must contain the `dataset$dataname`. Or can also be an object |
|||
11 |
- #'+ #' of class `PythonCodeClass` returned by [`python_code`]. |
|||
12 |
- #' @param dataname (`character`)\cr+ #' @param script (`character`)\cr |
|||
13 |
- #' A given name for the dataset it may not contain spaces+ #' file that contains R Code that can be read using [`read_script`]. |
|||
14 |
- #'+ #' Preferred before `code` argument. |
|||
15 |
- #' @param pull_callable (`CallableFunction`)\cr+ #' @param vars (named `list`)) \cr |
|||
16 |
- #' function with necessary arguments set to fetch data from connection.+ #' In case when this object code depends on other `TealDataset` object(s) or |
|||
17 |
- #'+ #' other constant value, this/these object(s) should be included as named |
|||
18 |
- #' @param keys (`character`)\cr+ #' element(s) of the list. For example if this object code needs `ADSL` |
|||
19 |
- #' vector of dataset primary keys column names+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
|||
20 |
- #'+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
|||
21 |
- #' @param parent optional, (`character`) \cr+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
|||
22 |
- #' parent dataset name+ #' are included to this object as local `vars` and they cannot be modified |
|||
23 |
- #'+ #' within another dataset. |
|||
24 |
- #' @param label (`character`)\cr+ #' @param ... not used, only for support of S3 |
|||
25 |
- #' Label to describe the dataset.+ #' |
|||
26 |
- #'+ #' @return modified `x` object |
|||
27 |
- #' @param code (`character`)\cr+ #' |
|||
28 |
- #' A character string defining code to modify `raw_data` from this dataset. To modify+ #' @export |
|||
29 |
- #' current dataset code should contain at least one assignment to object defined in `dataname`+ mutate_dataset <- function(x, ...) { |
|||
30 | -+ | 64x |
- #' argument. For example if `dataname = ADSL` example code should contain+ UseMethod("mutate_dataset") |
|
31 |
- #' `ADSL <- <some R code>`. Can't be used simultaneously with `script`+ } |
|||
32 |
- #'+ |
|||
33 |
- #' @param script (`character`)\cr+ #' @rdname mutate_dataset |
|||
34 |
- #' Alternatively to `code` - location of the file containing modification code.+ #' @examples |
|||
35 |
- #' Can't be used simultaneously with `script`.+ #' library(magrittr) |
|||
37 |
- #' @param vars (named `list`)) \cr+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
|||
38 |
- #' In case when this object code depends on other `TealDataset` object(s) or+ #' |
|||
39 |
- #' other constant value, this/these object(s) should be included as named+ #' ADSL_dataset <- dataset( |
|||
40 |
- #' element(s) of the list. For example if this object code needs `ADSL`+ #' dataname = "ADSL", |
|||
41 |
- #' object we should specify `vars = list(ADSL = <adsl object>)`.+ #' x = ADSL, |
|||
42 |
- #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to+ #' label = "AdAM subject-level dataset", |
|||
43 |
- #' the `vars` list to preserve reproducibility. Please note that `vars`+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")" |
|||
44 |
- #' are included to this object as local `vars` and they cannot be modified+ #' ) |
|||
45 |
- #' within another dataset.+ #' ADSL_mutated <- ADSL_dataset %>% |
|||
46 |
- #'+ #' mutate_dataset(code = "ADSL$new_variable <- 1") |
|||
47 |
- #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr+ #' |
|||
48 |
- #' Field containing either the metadata about the dataset (each element of the list+ #' ADSL_mutated$get_raw_data()$new_variable[1] |
|||
49 |
- #' should be atomic and length one) or a `CallableFuntion` to pull the metadata+ #' |
|||
50 |
- #' from a connection. This should return a `list` or an object which can be+ #' # Use an R script to mutate the data |
|||
51 |
- #' converted to a list with `as.list`.+ #' file_example <- tempfile(fileext = ".R") |
|||
52 |
- CDISCTealDatasetConnector <- R6::R6Class( # nolint+ #' writeLines( |
|||
53 |
- classname = "CDISCTealDatasetConnector",+ #' text = c( |
|||
54 |
- inherit = TealDatasetConnector,+ #' "ADSL <- ADSL %>% |
|||
55 |
-
+ #' dplyr::mutate(new_variable = new_variable * 2)" |
|||
56 |
- ## __Public Methods ====+ #' ), |
|||
57 |
- public = list(+ #' con = file_example |
|||
58 |
- #' @description+ #' ) |
|||
59 |
- #' Create a new `TealDatasetConnector` object. Set the pulling function+ #' |
|||
60 |
- #' `CallableFunction` which returns a `data.frame`, e.g. by reading+ #' ADSL_mutated <- ADSL_mutated %>% |
|||
61 |
- #' from a function or creating it on the fly.+ #' mutate_dataset(script = file_example) |
|||
62 |
- initialize = function(dataname,+ #' |
|||
63 |
- pull_callable,+ #' ADSL_mutated$get_raw_data()$new_variable[1] |
|||
64 |
- keys, parent,+ #' |
|||
65 |
- code = character(0),+ #' ADSL_mutated <- ADSL_mutated %>% |
|||
66 |
- label = character(0),+ #' mutate_dataset(code = read_script(file_example)) |
|||
67 |
- vars = list(),+ #' |
|||
68 |
- metadata = NULL) {+ #' ADSL_mutated$get_raw_data()$new_variable[1] |
|||
69 | -38x | +
- super$initialize(+ #' @export |
||
70 | -38x | +
- dataname = dataname,+ mutate_dataset.TealDataset <- function(x, |
||
71 | -38x | +
- pull_callable = pull_callable,+ code = character(0), |
||
72 | -38x | +
- keys = keys,+ script = character(0), |
||
73 | -38x | +
- code = code,+ vars = list(), |
||
74 | -38x | +
- label = label,+ ...) { |
||
75 | -38x | +31x |
- vars = vars,+ check_ellipsis(...) |
|
76 | -38x | +31x |
- metadata = metadata+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
77 |
- )+ |
|||
78 | -38x | +31x |
- private$set_parent(parent)+ code <- code_from_script(code, script) |
|
79 | -38x | +29x |
- logger::log_trace("CDISCTealDatasetConnector initialized for dataset: { deparse1(self$get_dataname()) }")+ x$mutate(code = code, vars = vars, ...) |
|
80 |
-
+ } |
|||
81 | -38x | +
- return(invisible(self))+ |
||
82 |
- },+ |
|||
83 |
- #' @description+ #' @rdname mutate_dataset |
|||
84 |
- #' Get parent dataset name+ #' @export |
|||
85 |
- #' @return (`character`) indicating parent `dataname`+ mutate_dataset.TealDatasetConnector <- function(x, # nolint |
|||
86 |
- get_parent = function() {+ code = character(0), |
|||
87 | -49x | +
- private$parent+ script = character(0), |
||
88 |
- },+ vars = list(), |
|||
89 |
-
+ ...) { |
|||
90 | -+ | 29x |
- #' @description+ check_ellipsis(...) |
|
91 | -+ | 29x |
- #' Pull the data+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
92 | -+ | 29x |
- #'+ code <- code_from_script(code, script) |
|
93 | -+ | 29x |
- #' Read or create the data using `pull_callable` specified in the constructor.+ x$mutate(code = code, vars = vars, ...) |
|
94 |
- #'+ } |
|||
95 |
- #' @param args (`NULL` or named `list`)\cr+ |
|||
96 |
- #' additional dynamic arguments for pull function. `args` can be omitted if `pull_callable`+ |
|||
97 |
- #' from constructor already contains all necessary arguments to pull data. One can try+ #' @rdname mutate_dataset |
|||
98 |
- #' to execute `pull_callable` directly by `x$pull_callable$run()` or to get code using+ #' @export |
|||
99 |
- #' `x$pull_callable$get_code()`. `args` specified in pull are used temporary to get data but+ mutate_dataset.TealDataAbstract <- function(x, |
|||
100 |
- #' not saved in code.+ dataname, |
|||
101 |
- #' @param try (`logical` value)\cr+ code = character(0), |
|||
102 |
- #' whether perform function evaluation inside `try` clause+ script = character(0), |
|||
103 |
- #'+ vars = list(), |
|||
104 |
- #' @return `self` invisibly for chaining.+ ...) { |
|||
105 | -+ | 4x |
- pull = function(args = NULL, try = FALSE) {+ check_ellipsis(...) |
|
106 | -28x | +4x |
- logger::log_trace("CDISCTealDatasetConnector$pull pulling dataset: { deparse1(self$get_dataname()) }.")+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
|
107 | -28x | +
- super$pull(args = args, try = try)+ |
||
108 | -+ | 4x |
-
+ code <- code_from_script(code, script) |
|
109 | -27x | +4x | +
+ x$mutate_dataset(dataname = dataname, code = code, vars = vars)+ |
+ |
110 | ++ |
+ }+ |
+ ||
111 | ++ | + + | +||
112 | ++ | + + | +||
113 | ++ | + + | +||
114 | ++ |
+ #' Mutate data by code+ |
+ ||
115 | ++ |
+ #'+ |
+ ||
116 | ++ |
+ #' @description `r lifecycle::badge("experimental")`+ |
+ ||
117 | ++ |
+ #' Code used in this mutation is not linked to particular+ |
+ ||
118 | ++ |
+ #' but refers to all datasets.+ |
+ ||
119 | ++ |
+ #' Consequence of this is that when using `get_code(<dataset>)` this+ |
+ ||
120 | ++ |
+ #' part of the code will be returned for each dataset specified. This method+ |
+ ||
121 | ++ |
+ #' should be used only if particular call involve changing multiple datasets.+ |
+ ||
122 | ++ |
+ #' Otherwise please use `mutate_dataset`.+ |
+ ||
123 | ++ |
+ #' Execution of the code is delayed after datasets are pulled+ |
+ ||
124 | ++ |
+ #' (`isTRUE(is_pulled)`).+ |
+ ||
125 | ++ |
+ #'+ |
+ ||
126 | +
- if (!self$is_failed()) {+ #' @param x (`TealDataAbstract`)\cr |
|||
110 | -27x | +|||
127 | +
- private$dataset <- as_cdisc(+ #' object. |
|||
111 | -27x | +|||
128 | +
- private$dataset,+ #' @inheritParams mutate_dataset |
|||
112 | -27x | +|||
129 | +
- parent = self$get_parent()+ #' |
|||
113 | +130 |
- )+ #' @return modified `x` object |
||
114 | -27x | +|||
131 | +
- logger::log_trace("CDISCTealDatasetConnector$pull pulled dataset: { deparse1(self$get_dataname()) }.")+ #' |
|||
115 | +132 |
- } else {+ #' @export |
||
116 | -! | +|||
133 | +
- logger::log_error("CDISCTealDatasetConnector$pull failed to pull dataset: { deparse1(self$get_dataname()) }.")+ mutate_data <- function(x, |
|||
117 | +134 |
- }+ code = character(0), |
||
118 | -27x | +|||
135 | +
- return(invisible(self))+ script = character(0), |
|||
119 | +136 |
- }+ vars = list()) {+ |
+ ||
137 | +3x | +
+ UseMethod("mutate_data") |
||
120 | +138 |
- ),+ } |
||
121 | +139 | |||
122 | +140 |
- ## __Private Fields ====+ #' @rdname mutate_data |
||
123 | +141 |
- private = list(+ #' @export |
||
124 | +142 |
- parent = character(0),+ mutate_data.TealDataAbstract <- function(x, |
||
125 | +143 |
-
+ code = character(0), |
||
126 | +144 |
- ## __Private Methods ====+ script = character(0), |
||
127 | +145 |
- set_parent = function(parent) {+ vars = list()) { |
||
128 | -38x | +146 | +3x |
- checkmate::assert_character(parent, max.len = 1, any.missing = FALSE)+ checkmate::assert_list(vars, min.len = 0, names = "unique") |
129 | -38x | +|||
147 | +
- private$parent <- parent+ |
|||
130 | -38x | +148 | +3x |
- return(invisible(self))+ code <- code_from_script(code, script) |
131 | -+ | |||
149 | +3x |
- }+ x$mutate(code = code, vars = vars) |
||
132 | -+ | |||
150 | +3x |
- )+ return(invisible(x)) |
||
133 | +151 |
- )+ } |
1 |
- #' Teal data+ #' Is pulled |
|||
4 |
- #' Universal function to pass data to teal application+ #' S3 method to determine if dataset is pulled (loaded). |
|||
6 |
- #' @param ... (`TealDataConnector`, `TealDataset`, `TealDatasetConnector`, `any`)\cr+ #' @param x ([`TealDatasetConnector`], [`TealDataset`] or [`TealDataAbstract`]) |
|||
7 |
- #' - When one of the `Teal*` objects are provided, then function returns `TealData` object.+ #' |
|||
8 |
- #' This way of specifying data is deprecated and will be removed in the next release.+ #' @return (`logical`) `TRUE` if connector has been already pulled, else `FALSE`. |
|||
9 |
- #' - From version 0.4.0, one can provide any object as a named argument and function will+ #' @export |
|||
10 |
- #' return `teal_data` object. Objects provided in `...` will be stored in `teal_data` environment+ is_pulled <- function(x) { |
|||
11 | -+ | 245x |
- #' under the same name as the argument name.+ UseMethod("is_pulled") |
|
12 |
- #' @param join_keys (`JoinKeys`) or a single (`JoinKeySet`)\cr+ } |
|||
13 |
- #' (optional) object with dataset column relationships used for joining.+ |
|||
14 |
- #' If empty then no joins between pairs of objects+ #' @rdname is_pulled |
|||
15 |
- #' @param code (`character`) code to reproduce the datasets.+ #' @export |
|||
16 |
- #' @param check (`logical`) reproducibility check - whether to perform a check that the pre-processing+ #' |
|||
17 |
- #' code included in the object definitions actually produces those objects.+ #' @examples |
|||
18 |
- #' If `check` is true and preprocessing code is empty an error will be thrown.+ #' # TealDatasetConnector -------- |
|||
19 |
- #'+ #' library(magrittr) |
|||
20 |
- #' @return (`TealData` or `teal_data`) object+ #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>% |
|||
21 |
- #'+ #' set_args(list(dataname = "ADSL")) |
|||
22 |
- #' @export+ #' x <- dataset_connector("ADSL", pull_fun_adsl) |
|||
24 |
- #' @examples+ #' is_pulled(x) |
|||
26 |
- #' teal_data(+ #' load_dataset(x) |
|||
27 |
- #' x1 = iris,+ #' is_pulled(x) |
|||
28 |
- #' x2 = mtcars,+ is_pulled.TealDatasetConnector <- function(x) { |
|||
29 | -+ | 59x |
- #' code = quote({+ return(x$is_pulled()) |
|
30 |
- #' x1 <- iris+ } |
|||
31 |
- #' x2 <- mtcars+ |
|||
32 |
- #' })+ #' @rdname is_pulled |
|||
33 |
- #' )+ #' @export |
|||
34 |
- teal_data <- function(...,+ #' |
|||
35 |
- join_keys = teal.data::join_keys(),+ #' @examples |
|||
36 |
- code = "",+ #' # TealDataset -------- |
|||
37 |
- check = FALSE) {- |
- |||
38 | -56x | -
- data_objects <- list(...)- |
- ||
39 | -56x | -
- if (inherits(join_keys, "JoinKeySet")) {- |
- ||
40 | -! | -
- join_keys <- teal.data::join_keys(join_keys)+ #' x <- dataset( |
||
41 | +38 |
- }+ #' dataname = "XY", |
||
42 | +39 |
- if (- |
- ||
43 | -56x | -
- checkmate::test_list(data_objects, types = c("TealDataConnector", "TealDataset", "TealDatasetConnector"))+ #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE), |
||
44 | +40 |
- ) {- |
- ||
45 | -46x | -
- lifecycle::deprecate_warn(- |
- ||
46 | -46x | -
- when = "0.3.1",- |
- ||
47 | -46x | -
- "cdisc_data(- |
- ||
48 | -46x | -
- data_objects = 'should use data directly. Using TealDatasetConnector and TealDataset is deprecated.'+ #' keys = "y", |
||
49 | +41 |
- )"+ #' code = "XY <- data.frame(x = c(1, 2), y = c('aa', 'bb'), |
||
50 | +42 |
- )- |
- ||
51 | -46x | -
- update_join_keys_to_primary(data_objects, join_keys)+ #' stringsAsFactors = FALSE)" |
||
52 | +43 | - - | -||
53 | -46x | -
- x <- TealData$new(..., check = check, join_keys = join_keys)- |
- ||
54 | -46x | -
- if (length(code) > 0 && !identical(code, "")) {- |
- ||
55 | -2x | -
- x$set_pull_code(code = code)+ #' ) |
||
56 | +44 |
- }- |
- ||
57 | -45x | -
- x$check_reproducibility()- |
- ||
58 | -44x | -
- x$check_metadata()- |
- ||
59 | -43x | -
- x+ #' |
||
60 | +45 |
- } else {- |
- ||
61 | -10x | -
- if (!checkmate::test_names(names(data_objects), type = "named")) {- |
- ||
62 | -! | -
- stop("Dot (`...`) arguments on `teal_data()` must be named.")+ #' is_pulled(x) |
||
63 | +46 |
- }- |
- ||
64 | -10x | -
- new_teal_data(- |
- ||
65 | -10x | -
- data = data_objects,- |
- ||
66 | -10x | -
- code = code,+ is_pulled.TealDataset <- function(x) { |
||
67 | -10x | -
- keys = join_keys- |
- ||
68 | -- |
- )- |
- ||
69 | -+ | 47 | +173x |
- }+ return(x$is_pulled()) |
70 | +48 |
} |
||
71 | +49 | |||
72 | -- |
- #' Load `TealData` object from a file- |
- ||
73 | -- |
- #'- |
- ||
74 | +50 |
- #' @description `r lifecycle::badge("experimental")`+ #' @rdname is_pulled |
||
75 | +51 |
- #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise.+ #' @export |
||
76 | +52 |
#' |
||
77 | -- |
- #' @param path A (`connection`) or a (`character`)\cr- |
- ||
78 | -- |
- #' string giving the pathname of the file or URL to read from. "" indicates the connection `stdin`.- |
- ||
79 | -- |
- #' @param code (`character`)\cr- |
- ||
80 | +53 |
- #' reproducible code to re-create object+ #' @examples |
||
81 | +54 |
#' |
||
82 | -- |
- #' @return `TealData` object- |
- ||
83 | +55 |
- #'+ #' library(magrittr) |
||
84 | +56 |
#' |
||
85 | +57 |
- #' @export+ #' # TealData -------- |
||
86 | +58 |
- #'+ #' x1 <- dataset( |
||
87 | +59 |
- #' @examples+ #' x = teal.data::example_cdisc_data("ADSL"), |
||
88 | +60 |
- #' # simple example+ #' dataname = "ADSL", |
||
89 | +61 |
- #' file_example <- tempfile(fileext = ".R")+ #' keys = get_cdisc_keys("ADSL"), |
||
90 | +62 |
- #' writeLines(+ #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")", |
||
91 | +63 |
- #' text = c(+ #' label = "ADTTE dataset" |
||
92 | +64 |
- #' "library(teal.data)+ #' ) |
||
93 | +65 |
#' |
||
94 | +66 |
- #' x1 <- dataset(dataname = \"IRIS\",+ #' x2 <- dataset( |
||
95 | +67 |
- #' x = iris,+ #' x = teal.data::example_cdisc_data("ADTTE"), |
||
96 | +68 |
- #' code = \"IRIS <- iris\")+ #' dataname = "ADTTE", |
||
97 | +69 |
- #'+ #' keys = get_cdisc_keys("ADTTE"), |
||
98 | +70 |
- #' x2 <- dataset(dataname = \"MTCARS\",+ #' code = "ADTTE <- teal.data::example_cdisc_data(\"ADTTE\")", |
||
99 | +71 |
- #' x = mtcars,+ #' label = "ADTTE dataset" |
||
100 | +72 |
- #' code = \"MTCARS <- mtcars\")+ #' ) |
||
101 | +73 |
#' |
||
102 | +74 |
- #' teal_data(x1, x2)"+ #' rd <- teal_data(x1, x2) |
||
103 | +75 |
- #' ),+ #' is_pulled(rd) |
||
104 | +76 |
- #' con = file_example+ #' |
||
105 | +77 |
- #' )+ #' # TealDataConnector -------- |
||
106 | +78 |
- #' teal_data_file(file_example, code = character(0))+ #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>% |
||
107 | +79 |
- teal_data_file <- function(path, code = get_code(path)) {+ #' set_args(list(dataname = "ADSL")) |
||
108 | -2x | +|||
80 | +
- object <- object_file(path, "TealData")+ #' adsl <- cdisc_dataset_connector( |
|||
109 | -2x | +|||
81 | +
- object$mutate(code)+ #' dataname = "ADSL", |
|||
110 | -2x | +|||
82 | +
- return(object)+ #' pull_callable = adsl_cf, |
|||
111 | +83 |
- }+ #' keys = get_cdisc_keys("ADSL") |
||
112 | +84 |
-
+ #' ) |
||
113 | +85 |
- #' Add primary keys as join_keys to a dataset self+ #' |
||
114 | +86 |
- #'+ #' new_cf <- callable_function(function(x) { |
||
115 | +87 |
- #' @param data_objects (`list`) of `TealDataset`, `TealDatasetConnector` or `TealDataConnector` objects+ #' x$NEW <- 1:nrow(x) |
||
116 | +88 |
- #' @param join_keys (`JoinKeys`) object+ #' x |
||
117 | +89 |
- #'+ #' }) |
||
118 | +90 |
- #' @keywords internal+ #' new_cf$set_args(list(x = as.name("x"))) |
||
119 | +91 |
- update_join_keys_to_primary <- function(data_objects, join_keys) {+ #' new <- cdisc_dataset_connector( |
||
120 | -79x | +|||
92 | +
- lapply(data_objects, function(obj) {+ #' dataname = "NEW", |
|||
121 | -142x | +|||
93 | +
- if (inherits(obj, "TealDataConnector")) {+ #' pull_callable = new_cf, |
|||
122 | -9x | +|||
94 | +
- update_join_keys_to_primary(obj$get_items(), join_keys)+ #' keys = get_cdisc_keys("ADSL"), |
|||
123 | +95 |
- } else {+ #' vars = list(x = adsl) |
||
124 | -133x | +|||
96 | +
- dataname <- obj$get_dataname()+ #' ) |
|||
125 | -133x | +|||
97 | +
- if (length(join_keys$get(dataname, dataname)) == 0) {+ #' |
|||
126 | -91x | +|||
98 | +
- join_keys$mutate(+ #' rdc <- cdisc_data(adsl, new) |
|||
127 | -91x | +|||
99 | +
- dataname,+ #' |
|||
128 | -91x | +|||
100 | +
- dataname,+ #' is_pulled(rdc) |
|||
129 | -91x | +|||
101 | +
- obj$get_keys()+ #' \dontrun{ |
|||
130 | +102 |
- )+ #' load_datasets(rdc) |
||
131 | +103 |
- }+ #' is_pulled(rdc) |
||
132 | +104 |
- }+ #' } |
||
133 | +105 |
- })+ is_pulled.TealDataAbstract <- function(x) { # nolint+ |
+ ||
106 | +13x | +
+ return(x$is_pulled()) |
||
134 | +107 |
}@@ -69171,14 +69149,14 @@ teal.data coverage - 74.87% |
1 |
- #' Retrieve raw data+ ## CDISCTealDataset ==== |
||
3 |
- #' @param x (`TealDataset`, `TealDatasetConnector`, `TealDataAbstract`)\cr+ #' @title R6 Class representing a dataset with parent attribute |
||
4 |
- #' object+ #' |
||
5 |
- #' @param dataname (`character`)\cr+ #' @description `r lifecycle::badge("stable")` |
||
6 |
- #' Name of dataset to return raw data for.+ #' Any `data.frame` object can be stored inside this object. |
||
8 |
- #' @description `r lifecycle::badge("stable")`+ #' The difference compared to `TealDataset` class is a parent field that |
||
9 |
- #'+ #' indicates name of the parent dataset. Note that the parent field might |
||
10 |
- #' @return `data.frame` with the raw data inserted into the R6 objects. In case of+ #' be empty (i.e. `character(0)`). |
||
11 |
- #' `TealDataAbstract`, `list` of `data.frame` can be returned+ #' |
||
12 |
- #' if user doesn't specify `dataname` - (`get_raw_data` from all datasets).+ #' @param dataname (`character`)\cr |
||
13 |
- #'+ #' A given name for the dataset it may not contain spaces |
||
14 |
- #' @export+ #' |
||
15 |
- get_raw_data <- function(x, dataname = NULL) {+ #' @param x (`data.frame`)\cr |
||
16 | -214x | +
- checkmate::assert_string(dataname, null.ok = TRUE)+ #' |
|
17 | -213x | +
- UseMethod("get_raw_data")+ #' @param keys (`character`)\cr |
|
18 |
- }+ #' vector with primary keys |
||
19 |
-
+ #' |
||
20 |
- #' @export+ #' @param parent optional, (`character`) \cr |
||
21 |
- #' @rdname get_raw_data+ #' parent dataset name |
||
22 |
- #' @examples+ #' |
||
23 |
- #'+ #' @param code (`character`)\cr |
||
24 |
- #' # TealDataset ---------+ #' A character string defining the code needed to produce the data set in `x` |
||
25 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ #' |
||
26 |
- #'+ #' @param label (`character`)\cr |
||
27 |
- #' x <- dataset(dataname = "ADSL", x = ADSL)+ #' Label to describe the dataset |
||
28 |
- #' get_raw_data(x)+ #' |
||
29 |
- get_raw_data.TealDataset <- function(x, dataname = NULL) {+ #' @param vars (named `list`)) \cr |
||
30 | -192x | +
- if (!is.null(dataname)) {+ #' In case when this object code depends on other `TealDataset` object(s) or |
|
31 | -2x | +
- warning("'dataname' argument ignored - TealDataset can contain only one dataset.")+ #' other constant value, this/these object(s) should be included as named |
|
32 |
- }+ #' element(s) of the list. For example if this object code needs `ADSL` |
||
33 | -192x | +
- x$get_raw_data()+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
|
34 |
- }+ #' It is recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
35 |
-
+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
||
36 |
- #' @export+ #' are included to this object as local `vars` and they cannot be modified |
||
37 |
- #' @rdname get_raw_data+ #' within another dataset. |
||
38 |
- #' @examples+ #' |
||
39 |
- #'+ #' @param metadata (named `list` or `NULL`) \cr |
||
40 |
- #' # TealDatasetConnector ---------+ #' Field containing metadata about the dataset. Each element of the list |
||
41 |
- #' library(magrittr)+ #' should be atomic and length one. |
||
42 |
- #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>%+ #' |
||
43 |
- #' set_args(list(dataname = "ADSL"))+ #' @examples |
||
44 |
- #' dc <- dataset_connector("ADSL", pull_fun_adsl)+ #' x <- cdisc_dataset( |
||
45 |
- #' load_dataset(dc)+ #' dataname = "XYZ", |
||
46 |
- #' get_raw_data(dc)+ #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE), |
||
47 |
- get_raw_data.TealDatasetConnector <- function(x, dataname = NULL) { # nolint+ #' keys = "y", |
||
48 | -17x | +
- if (!is.null(dataname)) {+ #' parent = "ABC", |
|
49 | -1x | +
- warning("'dataname' argument ignored - TealDatasetConnector can contain only one dataset.")+ #' code = "XYZ <- data.frame(x = c(1, 2), y = c('aa', 'bb'), |
|
50 |
- }+ #' stringsAsFactors = FALSE)", |
||
51 | -17x | +
- x$get_raw_data()+ #' metadata = list(type = "example") |
|
52 |
- }+ #' ) |
||
53 |
-
+ #' |
||
54 |
- #' @rdname get_raw_data+ #' x$ncol |
||
55 |
- #' @export+ #' x$get_code() |
||
56 |
- #' @examples+ #' x$get_dataname() |
||
57 |
- #'+ #' x$get_keys() |
||
58 |
- #' # TealData ----------------+ #' x$get_parent() |
||
59 |
- #' adsl <- cdisc_dataset(+ CDISCTealDataset <- R6::R6Class( # nolint |
||
60 |
- #' dataname = "ADSL",+ "CDISCTealDataset", |
||
61 |
- #' x = teal.data::example_cdisc_data("ADSL"),+ inherit = TealDataset, |
||
62 |
- #' code = "library(teal.data)\nADSL <- teal.data::example_cdisc_data(\"ADSL\")"+ ## __Public Methods ==== |
||
63 |
- #' )+ public = list( |
||
64 |
- #'+ #' @description |
||
65 |
- #' adtte <- cdisc_dataset(+ #' Create a new object of `CDISCTealDataset` class |
||
66 |
- #' dataname = "ADTTE",+ initialize = function(dataname, x, keys, parent, code = character(0), |
||
67 |
- #' x = teal.data::example_cdisc_data("ADTTE"),+ label = character(0), vars = list(), metadata = NULL) { |
||
68 | -+ | 81x |
- #' code = "library(teal.data)\nADTTE <- teal.data::example_cdisc_data(\"ADTTE\")"+ checkmate::assert_character(parent, max.len = 1, any.missing = FALSE) |
69 | -+ | 80x |
- #' )+ super$initialize( |
70 | -+ | 80x |
- #'+ dataname = dataname, x = x, keys = keys, code = code, |
71 | -+ | 80x |
- #' rd <- teal.data:::TealData$new(adsl, adtte)+ label = label, vars = vars, metadata = metadata |
72 |
- #' get_raw_data(rd)+ ) |
||
73 |
- #'+ |
||
74 | -+ | 80x |
- #' # TealDataConnector --------+ self$set_parent(parent) |
75 | -+ | 80x |
- #' library(magrittr)+ logger::log_trace("CDISCTealDataset initialized for dataset: { deparse1(self$get_dataname()) }.") |
76 | -+ | 80x |
- #'+ return(invisible(self)) |
77 |
- #' slice_cdisc_data <- function(dataname, n) {+ }, |
||
78 |
- #' head(example_cdisc_data(dataname), n)+ #' @description |
||
79 |
- #' }+ #' Recreate a dataset with its current attributes |
||
80 |
- #'+ #' This is useful way to have access to class initialize method basing on class object |
||
81 |
- #' random_data_connector <- function(dataname) {+ #' |
||
82 |
- #' fun_dataset_connector(+ #' @return a new object of `CDISCTealDataset` class |
||
83 |
- #' dataname = dataname,+ recreate = function(dataname = self$get_dataname(), |
||
84 |
- #' fun = slice_cdisc_data,+ x = self$get_raw_data(), |
||
85 |
- #' fun_args = list(dataname = dataname),+ keys = self$get_keys(), |
||
86 |
- #' )+ parent = self$get_parent(), |
||
87 |
- #' }+ code = private$code, |
||
88 |
- #'+ label = self$get_dataset_label(), |
||
89 |
- #' open_fun <- callable_function(library)+ vars = list(), |
||
90 |
- #' open_fun$set_args(list(package = "teal.data"))+ metadata = self$get_metadata()) { |
||
91 | -+ | 8x |
- #'+ res <- self$initialize( |
92 | -+ | 8x |
- #' con <- data_connection(open_fun = open_fun)+ dataname = dataname, |
93 | -+ | 8x |
- #' con$set_open_server(+ x = x, |
94 | -+ | 8x |
- #' function(id, connection) {+ keys = keys, |
95 | -+ | 8x |
- #' moduleServer(+ parent = parent, |
96 | -+ | 8x |
- #' id = id,+ code = code, |
97 | -+ | 8x |
- #' module = function(input, output, session) {+ label = label, |
98 | -+ | 8x |
- #' connection$open(try = TRUE)+ vars = vars, |
99 | -+ | 8x |
- #' return(invisible(connection))+ metadata = metadata |
100 |
- #' }+ ) |
||
101 | -+ | 8x |
- #' )+ logger::log_trace("CDISCTealDataset$recreate recreated dataset: { deparse1(self$get_dataname()) }.") |
102 | -+ | 8x |
- #' }+ return(res) |
103 |
- #' )+ }, |
||
104 |
- #'+ #' @description |
||
105 |
- #' rdc <- relational_data_connector(+ #' Get all dataset attributes |
||
106 |
- #' connection = con,+ #' @return (named `list`) with dataset attributes |
||
107 |
- #' connectors = list(random_data_connector("ADSL"), random_data_connector("ADLB"))+ get_attrs = function() { |
||
108 | -+ | ! |
- #' )+ x <- super$get_attrs() |
109 | -+ | ! |
- #'+ x <- append( |
110 | -+ | ! |
- #' rdc$set_ui(+ x, |
111 | -+ | ! |
- #' function(id, connection, connectors) {+ list( |
112 | -+ | ! |
- #' ns <- NS(id)+ parent = self$get_parent() |
113 |
- #' tagList(+ ) |
||
114 |
- #' connection$get_open_ui(ns("open_connection")),+ ) |
||
115 | -+ | ! |
- #' numericInput(inputId = ns("n"), label = "Choose number of records", min = 0, value = 1),+ return(x) |
116 |
- #' do.call(+ }, |
||
117 |
- #' what = "tagList",+ #' @description |
||
118 |
- #' args = lapply(+ #' Get parent dataset name |
||
119 |
- #' connectors,+ #' @return (`character`) indicating parent `dataname` |
||
120 |
- #' function(connector) {+ get_parent = function() { |
||
121 | -+ | 38x |
- #' div(+ return(private$parent) |
122 |
- #' connector$get_ui(+ }, |
||
123 |
- #' id = ns(connector$get_dataname())+ #' @description |
||
124 |
- #' ),+ #' Set parent dataset name |
||
125 |
- #' br()+ #' @param parent (`character`) indicating parent `dataname` |
||
126 |
- #' )+ #' @return (`self`) invisibly for chaining |
||
127 |
- #' }+ set_parent = function(parent) { |
||
128 | -+ | 81x |
- #' )+ checkmate::assert_character(parent, max.len = 1, any.missing = FALSE) |
129 | -+ | 81x |
- #' )+ private$parent <- parent |
130 |
- #' )+ |
||
131 | -+ | 81x |
- #' }+ logger::log_trace("CDISCTealDataset$set_parent parent set for dataset: { deparse1(self$get_dataname()) }.") |
132 | -+ | 81x |
- #' )+ return(invisible(self)) |
133 |
- #'+ } |
||
134 |
- #' rdc$set_server(+ ), |
||
135 |
- #' function(id, connection, connectors) {+ ## __Private Fields ==== |
||
136 |
- #' moduleServer(+ private = list( |
||
137 |
- #' id = id,+ parent = character(0) |
||
138 |
- #' module = function(input, output, session) {+ ) |
||
139 |
- #' # opens connection+ ) |
||
140 |
- #' connection$get_open_server()(id = "open_connection", connection = connection)+ |
||
141 |
- #' if (connection$is_opened()) {+ # constructors ==== |
||
142 |
- #' for (connector in connectors) {+ #' Create a new object of `CDISCTealDataset` class |
||
143 |
- #' set_args(connector, args = list(n = input$n))+ #' |
||
144 |
- #' # pull each dataset+ #' @description `r lifecycle::badge("stable")` |
||
145 |
- #' connector$get_server()(id = connector$get_dataname())+ #' Function that creates `CDISCTealDataset` object |
||
146 |
- #' if (connector$is_failed()) {+ #' |
||
147 |
- #' break+ #' @inheritParams dataset |
||
148 |
- #' }+ #' @param parent (`character`, optional) parent dataset name |
||
149 |
- #' }+ #' |
||
150 |
- #' }+ #' @return (`CDISCTealDataset`) a dataset with connected metadata |
||
151 |
- #' }+ #' |
||
152 |
- #' )+ #' @export |
||
153 |
- #' }+ #' |
||
154 |
- #' )+ #' @examples |
||
155 |
- #'+ #' ADSL <- example_cdisc_data("ADSL") |
||
156 |
- #' \dontrun{+ #' |
||
157 |
- #' load_datasets(rdc)+ #' cdisc_dataset("ADSL", ADSL, metadata = list(type = "teal.data")) |
||
158 |
- #' get_raw_data(rdc)+ cdisc_dataset <- function(dataname, |
||
159 |
- #' }+ x, |
||
160 |
- #'+ keys = get_cdisc_keys(dataname), |
||
161 |
- #' # TealData (with connectors) --------+ parent = `if`(identical(dataname, "ADSL"), character(0), "ADSL"), |
||
162 |
- #' drc <- cdisc_data(rdc)+ label = data_label(x), |
||
163 |
- #' \dontrun{+ code = character(0), |
||
164 |
- #' get_raw_data(drc)+ vars = list(), |
||
165 |
- #' }+ metadata = NULL) { |
||
166 | -+ | 66x |
- get_raw_data.TealDataAbstract <- function(x, dataname = NULL) { # nolint+ CDISCTealDataset$new( |
167 | -4x | +66x |
- if (!is.null(dataname)) {+ dataname = dataname, |
168 | -! | +66x |
- datasets_names <- x$get_datanames()+ x = x, |
169 | -! | +66x |
- if (dataname %in% datasets_names) {+ keys = keys, |
170 | -! | +66x |
- if (is_pulled(x$get_items(dataname))) {+ parent = parent, |
171 | -! | +66x |
- get_raw_data(+ label = label, |
172 | -! | +66x |
- get_dataset(x, dataname = dataname)+ code = code, |
173 | -+ | 66x |
- )+ vars = vars, |
174 | -+ | 66x |
- } else {+ metadata = metadata |
175 | -! | +
- stop(+ ) |
|
176 | -! | +
- sprintf("'%s' has not been pulled yet\n - please use `load_dataset()` first.", dataname),+ } |
|
177 | -! | +
- call. = FALSE+ |
|
178 |
- )+ #' Load `CDISCTealDataset` object from a file |
||
179 |
- }+ #' |
||
180 |
- } else {+ #' @description `r lifecycle::badge("experimental")` |
||
181 | -! | +
- stop("The dataname supplied does not exist.")+ #' Please note that the script has to end with a call creating desired object. The error will be raised otherwise. |
|
182 |
- }+ #' |
||
183 |
- } else {+ #' @inheritParams dataset_file |
||
184 | -4x | +
- lapply(+ #' |
|
185 | -4x | +
- get_datasets(x),+ #' @return (`CDISCTealDataset`) object |
|
186 | -4x | +
- get_raw_data+ #' |
|
187 |
- )+ #' @export |
||
188 |
- }+ #' |
||
189 |
- }+ #' @examples |
1 | +190 |
- #' Get dataset attributes+ #' # simple example |
|
2 | +191 |
- #'+ #' file_example <- tempfile(fileext = ".R") |
|
3 | +192 |
- #' @description `r lifecycle::badge("stable")`+ #' writeLines( |
|
4 | +193 |
- #' Get dataset attributes in form of named list.+ #' text = c( |
|
5 | +194 |
- #'+ #' "library(teal.data) |
|
6 | +195 |
- #' @param x an object of (`TealDataset`) class+ #' cdisc_dataset(dataname = \"ADSL\", |
|
7 | +196 |
- #'+ #' x = teal.data::example_cdisc_data(\"ADSL\"), |
|
8 | +197 |
- #' @return named `list` of object attributes+ #' code = \"ADSL <- teal.data::example_cdisc_data('ADSL')\")" |
|
9 | +198 |
- #'+ #' ), |
|
10 | +199 |
- #' @export+ #' con = file_example |
|
11 | +200 |
- get_attrs <- function(x) {+ #' )+ |
+ |
201 | ++ |
+ #' x <- cdisc_dataset_file(file_example, code = character(0))+ |
+ |
202 | ++ |
+ #' get_code(x)+ |
+ |
203 | ++ |
+ cdisc_dataset_file <- function(path, code = get_code(path)) { |
|
12 | +204 | ! |
- UseMethod("get_attrs")+ object <- object_file(path, "CDISCTealDataset")+ |
+
205 | +! | +
+ object$set_code(code)+ |
+ |
206 | +! | +
+ return(object) |
|
13 | +207 |
} |
14 | +1 |
-
+ #' S3 method for getting a label of |
|
15 | +2 |
-
+ #' (`TealDatasetConnector` or `TealDataset`) R6 object |
|
16 | +3 |
- #' @rdname get_attrs+ #' |
|
17 | +4 |
- #' @export+ #' @description `r lifecycle::badge("stable")` |
|
18 | +5 |
- #' @examples+ #' |
|
19 | +6 |
- #' # TealDataset --------+ #' @param x (`TealDatasetConnector` or `TealDataset`) R6 object |
|
20 | +7 |
#' |
|
21 | +8 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ #' @return label (`character`) Label to describe the dataset |
|
22 | +9 |
- #'+ #' @export |
|
23 | +10 |
- #' x1 <- dataset("ADSL", x = ADSL, label = "custom label")+ get_dataset_label <- function(x) {+ |
+ |
11 | +48x | +
+ UseMethod("get_dataset_label") |
|
24 | +12 |
- #' get_attrs(x1)+ } |
|
25 | +13 |
- #'+ |
|
26 | +14 |
- #' x2 <- dataset(+ #' @rdname get_dataset_label |
|
27 | +15 |
- #' "ADSL",+ #' @export |
|
28 | +16 |
- #' x = ADSL,+ #' @examples |
|
29 | +17 |
- #' keys = get_cdisc_keys("ADSL"),+ #' fun <- callable_function(data.frame) |
|
30 | +18 |
- #' label = "custom label"+ #' fun$set_args(list(c1 = seq_len(10))) |
|
31 | +19 |
- #' )+ #' |
|
32 | +20 |
- #' get_attrs(x2)+ #' x <- dataset_connector( |
|
33 | +21 |
- #'+ #' pull_callable = fun, |
|
34 | +22 |
- #' # CDISCTealDataset --------+ #' dataname = "ADSL", |
|
35 | +23 |
- #'+ #' label = "My custom label" |
|
36 | +24 | ++ |
+ #' )+ |
+
25 | ++ |
+ #' get_dataset_label(x)+ |
+ |
26 | ++ |
+ get_dataset_label.TealDatasetConnector <- function(x) { # nolint+ |
+ |
27 | +17x | +
+ return(x$get_dataset_label())+ |
+ |
28 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ } |
||
37 | +29 |
- #' x3 <- cdisc_dataset(+ |
|
38 | +30 |
- #' "ADSL",+ #' @rdname get_dataset_label |
|
39 | +31 |
- #' x = ADSL,+ #' @export |
|
40 | +32 |
- #' keys = get_cdisc_keys("ADSL"),+ #' @examples |
|
41 | +33 |
- #' label = "custom label"+ #' ADSL <- example_cdisc_data("ADSL") |
|
42 | +34 |
- #' )+ #' ADSL_dataset <- dataset(dataname = "ADSL", x = ADSL, label = "My custom label") |
|
43 | +35 |
- #' get_attrs(x3)+ #' get_dataset_label(ADSL_dataset) |
|
44 | +36 |
- get_attrs.TealDataset <- function(x) {+ get_dataset_label.TealDataset <- function(x) { |
|
45 | -! | +||
37 | +31x |
- return(x$get_attrs())+ return(x$get_dataset_label()) |
|
46 | +38 |
}@@ -70828,28 +70876,28 @@ teal.data coverage - 74.87% |
1 |
- #' Get dataset from `TealDatasetConnector`+ #' S3 method for getting a `dataname(s)` of |
||
2 |
- #'+ #' (`TealDataAbstract`, (`TealDatasetConnector` or |
||
3 |
- #' @description `r lifecycle::badge("stable")`+ #' `TealDataset`) R6 object |
||
5 |
- #' Get dataset from `TealDatasetConnector`+ #' @description `r lifecycle::badge("stable")` |
||
6 |
- #' @param x (`TealDatasetConnector` or `TealDatasetConnector` or `TealDataAbstract`)+ #' |
||
7 |
- #' @param dataname (`character`) a name of dataset to be retrieved+ #' @param x (`TealDataAbstract`, `TealDatasetConnector` or |
||
8 |
- #' @details See `help(TealDataConnector)` and `help(TealData)` for more complex examples.+ #' `TealDataset`) object |
||
9 |
- #' @return (`TealDataset`)+ #' |
||
10 |
- #' @export+ #' @return `dataname` (`character`) A given name for the dataset(s) |
||
11 |
- get_dataset <- function(x, dataname) {+ #' it may not contain spaces |
||
12 | -131x | +
- UseMethod("get_dataset")+ #' @export |
|
13 |
- }+ get_dataname <- function(x) { |
||
14 | -+ | 731x |
-
+ UseMethod("get_dataname") |
15 |
- #' @rdname get_dataset+ } |
||
16 |
- #' @export+ |
||
17 |
- #' @examples+ #' @rdname get_dataname |
||
18 |
- #'+ #' @export |
||
19 |
- #' # TealDatasetConnector --------+ get_dataname.TealDataAbstract <- function(x) { # nolint |
||
20 | -+ | 15x |
- #' library(magrittr)+ return(x$get_datanames()) |
21 |
- #'+ } |
||
22 |
- #' pull_fun_adae <- callable_function(teal.data::example_cdisc_data) %>%+ |
||
23 |
- #' set_args(list(dataname = "ADAE"))+ #' @rdname get_dataname |
||
24 |
- #'+ #' @export |
||
25 |
- #' ADSL <- teal.data::example_cdisc_data("ADSL")+ get_dataname.TealDatasetConnector <- function(x) { # nolint |
||
26 | -+ | 213x |
- #'+ return(x$get_dataname()) |
27 |
- #' dc <- dataset_connector(+ } |
||
28 |
- #' dataname = "ADAE", pull_callable = pull_fun_adae,+ |
||
29 |
- #' keys = get_cdisc_keys("ADSL")+ |
||
30 |
- #' )+ #' @rdname get_dataname |
||
31 |
- #'+ #' @export |
||
32 |
- #' \dontrun{+ get_dataname.TealDataset <- function(x) { # nolint |
||
33 | -+ | 503x |
- #' load_dataset(dc)+ return(x$get_dataname()) |
34 |
- #' get_dataset(dc)+ } |
||
35 |
- #' }+ |
||
36 |
- #'+ #' @rdname get_dataname |
||
37 |
- get_dataset.TealDatasetConnector <- function(x, dataname = NULL) { # nolint+ #' @export |
||
38 | -47x | +
- if (!is.null(dataname)) {+ get_dataname.teal_data <- function(x) { # nolint |
|
39 | ! |
- warning("'dataname' argument ignored - TealDatasetConnector can contain only one dataset.")+ return(x@datanames) |
|
40 | - |
- }- |
- |
41 | -47x | -
- return(x$get_dataset())- |
- |
42 | -
} |
43 | -- | - - | -|
44 | +1 |
- #' @rdname get_dataset+ ## CDISCTealDatasetConnector ==== |
|
45 | +2 |
- #' @export+ #' |
|
46 | +3 |
- #' @examples+ #' @title A `CDISCTealDatasetConnector` class of objects |
|
47 | +4 |
#' |
|
48 | -- |
- #' # TealDataset --------- |
- |
49 | +5 |
- #' ADSL <- example_cdisc_data("ADSL")+ #' @description `r lifecycle::badge("stable")` |
|
50 | +6 |
- #' x <- dataset("ADSL", ADSL)+ #' Objects of this class store the connection function to fetch a single dataset. |
|
51 | +7 |
#' |
|
52 | -- |
- #' get_dataset(x)- |
- |
53 | -- |
- get_dataset.TealDataset <- function(x, dataname = NULL) { # nolint- |
- |
54 | -84x | -
- if (!is.null(dataname)) {- |
- |
55 | -! | -
- warning("'dataname' argument ignored - TealDataset can contain only one dataset.")- |
- |
56 | +8 |
- }- |
- |
57 | -84x | -
- return(x$get_dataset())+ #' The difference compared to `TealDatasetConnector` is a parent field that |
|
58 | +9 |
- }+ #' indicates name of the parent dataset. Note that the parent field might |
|
59 | +10 |
-
+ #' be empty (i.e. `character(0)`). |
|
60 | +11 |
- #' @rdname get_dataset+ #' |
|
61 | +12 |
- #' @export+ #' @param dataname (`character`)\cr |
|
62 | +13 |
- #' @examples+ #' A given name for the dataset it may not contain spaces |
|
63 | +14 |
#' |
|
64 | -- |
- #' # TealData (not containing connectors) --------- |
- |
65 | +15 |
- #' adsl <- cdisc_dataset(+ #' @param pull_callable (`CallableFunction`)\cr |
|
66 | +16 |
- #' dataname = "ADSL",+ #' function with necessary arguments set to fetch data from connection. |
|
67 | +17 |
- #' x = example_cdisc_data("ADSL"),+ #' |
|
68 | +18 |
- #' code = "library(teal.data)\nADSL <- example_cdisc_data(\"ADSL\")"+ #' @param keys (`character`)\cr |
|
69 | +19 |
- #' )+ #' vector of dataset primary keys column names |
|
70 | +20 |
#' |
|
71 | +21 |
- #' adae <- cdisc_dataset(+ #' @param parent optional, (`character`) \cr |
|
72 | +22 |
- #' dataname = "ADAE",+ #' parent dataset name |
|
73 | +23 |
- #' x = example_cdisc_data("ADAE"),+ #' |
|
74 | +24 |
- #' code = "library(teal.data)\nADAE <- example_cdisc_data(\"ADAE\")"+ #' @param label (`character`)\cr |
|
75 | +25 |
- #' )+ #' Label to describe the dataset. |
|
76 | +26 |
#' |
|
77 | -- |
- #' rd <- teal.data:::TealData$new(adsl, adae)- |
- |
78 | -- |
- #' get_dataset(rd, "ADSL")- |
- |
79 | +27 |
- get_dataset.TealDataAbstract <- function(x, dataname = NULL) {- |
- |
80 | -! | -
- if (is.null(dataname)) {- |
- |
81 | -! | -
- stop(paste(- |
- |
82 | -! | -
- "To get single dataset from data class one must specify the name of the dataset.",- |
- |
83 | -! | -
- "To get all datasets please use get_datasets()"+ #' @param code (`character`)\cr |
|
84 | +28 |
- ))+ #' A character string defining code to modify `raw_data` from this dataset. To modify |
|
85 | +29 |
- }- |
- |
86 | -! | -
- return(x$get_dataset(dataname = dataname))+ #' current dataset code should contain at least one assignment to object defined in `dataname` |
|
87 | +30 |
- }+ #' argument. For example if `dataname = ADSL` example code should contain |
1 | +31 |
- #' Helper function to deep copy `R6` object+ #' `ADSL <- <some R code>`. Can't be used simultaneously with `script` |
|
2 | +32 |
#' |
|
3 | +33 |
- #' When cloning an R6 object the private function+ #' @param script (`character`)\cr |
|
4 | +34 |
- #' `deep_clone` is automatically used. To ensure a complete+ #' Alternatively to `code` - location of the file containing modification code. |
|
5 | +35 |
- #' clone the private function should call this function+ #' Can't be used simultaneously with `script`. |
|
6 | +36 |
#' |
|
7 | -- |
- #' @param name (`character`) argument passed by `deep_clone` function.- |
- |
8 | -- |
- #' @param value (any `R` object) argument passed by `deep_clone` function.- |
- |
9 | +37 |
- #' @keywords internal+ #' @param vars (named `list`)) \cr |
|
10 | +38 |
- deep_clone_r6 <- function(name, value) {- |
- |
11 | -1629x | -
- if (checkmate::test_list(value, types = "R6")) {- |
- |
12 | -86x | -
- lapply(value, function(x) x$clone(deep = TRUE))- |
- |
13 | -1543x | -
- } else if (R6::is.R6(value)) {- |
- |
14 | -31x | -
- value$clone(deep = TRUE)- |
- |
15 | -1512x | -
- } else if (is.environment(value)) {- |
- |
16 | -5x | -
- new_env <- as.environment(as.list(value, all.names = TRUE))- |
- |
17 | -5x | -
- parent.env(new_env) <- parent.env(value)- |
- |
18 | -5x | -
- new_env+ #' In case when this object code depends on other `TealDataset` object(s) or |
|
19 | +39 |
- } else {- |
- |
20 | -1507x | -
- value+ #' other constant value, this/these object(s) should be included as named |
|
21 | +40 |
- }+ #' element(s) of the list. For example if this object code needs `ADSL` |
|
22 | +41 |
- }+ #' object we should specify `vars = list(ADSL = <adsl object>)`. |
1 | +42 |
- #' Function to get join keys from a `` object+ #' It's recommended to include `TealDataset` or `TealDatasetConnector` objects to |
||
2 | +43 |
- #' @param data `` - object to extract the join keys+ #' the `vars` list to preserve reproducibility. Please note that `vars` |
||
3 | +44 |
- #' @return Either `JoinKeys` object or `NULL` if no join keys+ #' are included to this object as local `vars` and they cannot be modified |
||
4 | +45 |
- #' @export+ #' within another dataset. |
||
5 | +46 |
- get_join_keys <- function(data) {+ #' |
||
6 | -14x | +|||
47 | +
- UseMethod("get_join_keys", data)+ #' @param metadata (named `list`, `NULL` or `CallableFunction`) \cr |
|||
7 | +48 |
- }+ #' Field containing either the metadata about the dataset (each element of the list |
||
8 | +49 |
-
+ #' should be atomic and length one) or a `CallableFuntion` to pull the metadata |
||
9 | +50 |
- #' @rdname get_join_keys+ #' from a connection. This should return a `list` or an object which can be |
||
10 | +51 |
- #' @export+ #' converted to a list with `as.list`. |
||
11 | +52 |
- get_join_keys.teal_data <- function(data) {+ CDISCTealDatasetConnector <- R6::R6Class( # nolint |
||
12 | -5x | +|||
53 | +
- data@join_keys+ classname = "CDISCTealDatasetConnector", |
|||
13 | +54 |
- }+ inherit = TealDatasetConnector, |
||
14 | +55 | |||
15 | +56 |
- #' @rdname get_join_keys+ ## __Public Methods ==== |
||
16 | +57 |
- #' @export+ public = list( |
||
17 | +58 |
- get_join_keys.JoinKeys <- function(data) {+ #' @description |
||
18 | -9x | +|||
59 | +
- data+ #' Create a new `TealDatasetConnector` object. Set the pulling function |
|||
19 | +60 |
- }+ #' `CallableFunction` which returns a `data.frame`, e.g. by reading |
||
20 | +61 |
-
+ #' from a function or creating it on the fly. |
||
21 | +62 |
- #' @rdname get_join_keys+ initialize = function(dataname, |
||
22 | +63 |
- #' @export+ pull_callable, |
||
23 | +64 |
- `get_join_keys<-` <- function(data, dataset_1, dataset_2 = NULL, value) {+ keys, parent, |
||
24 | -2x | +|||
65 | +
- UseMethod("get_join_keys<-", data)+ code = character(0), |
|||
25 | +66 |
- }+ label = character(0), |
||
26 | +67 |
-
+ vars = list(), |
||
27 | +68 |
- #' @rdname get_join_keys+ metadata = NULL) { |
||
28 | -+ | |||
69 | +38x |
- #' @export+ super$initialize( |
||
29 | -+ | |||
70 | +38x |
- `get_join_keys<-.JoinKeys` <- function(data, dataset_1, dataset_2 = NULL, value) {+ dataname = dataname, |
||
30 | -1x | +71 | +38x |
- data+ pull_callable = pull_callable, |
31 | -+ | |||
72 | +38x |
- }+ keys = keys, |
||
32 | -+ | |||
73 | +38x |
-
+ code = code, |
||
33 | -+ | |||
74 | +38x |
- #' @rdname get_join_keys+ label = label, |
||
34 | -+ | |||
75 | +38x |
- #' @export+ vars = vars,+ |
+ ||
76 | +38x | +
+ metadata = metadata |
||
35 | +77 |
- `get_join_keys<-.teal_data` <- function(data, dataset_1, dataset_2 = NULL, value) {+ ) |
||
36 | -1x | +78 | +38x |
- data+ private$set_parent(parent) |
37 | -+ | |||
79 | +38x |
- }+ logger::log_trace("CDISCTealDatasetConnector initialized for dataset: { deparse1(self$get_dataname()) }") |
1 | +80 |
- #' S3 method for getting a label of+ |
||
2 | -+ | |||
81 | +38x |
- #' (`TealDatasetConnector` or `TealDataset`) R6 object+ return(invisible(self)) |
||
3 | +82 |
- #'+ }, |
||
4 | +83 |
- #' @description `r lifecycle::badge("stable")`+ #' @description |
||
5 | +84 |
- #'+ #' Get parent dataset name |
||
6 | +85 |
- #' @param x (`TealDatasetConnector` or `TealDataset`) R6 object+ #' @return (`character`) indicating parent `dataname` |
||
7 | +86 |
- #'+ get_parent = function() { |
||
8 | -+ | |||
87 | +49x |
- #' @return label (`character`) Label to describe the dataset+ private$parent |
||
9 | +88 |
- #' @export+ }, |
||
10 | +89 |
- get_dataset_label <- function(x) {+ |
||
11 | -48x | +|||
90 | +
- UseMethod("get_dataset_label")+ #' @description |
|||
12 | +91 |
- }+ #' Pull the data |
||
13 | +92 |
-
+ #' |
||
14 | +93 |
- #' @rdname get_dataset_label+ #' Read or create the data using `pull_callable` specified in the constructor. |
||
15 | +94 |
- #' @export+ #' |
||
16 | +95 |
- #' @examples+ #' @param args (`NULL` or named `list`)\cr |
||
17 | +96 |
- #' fun <- callable_function(data.frame)+ #' additional dynamic arguments for pull function. `args` can be omitted if `pull_callable` |
||
18 | +97 |
- #' fun$set_args(list(c1 = seq_len(10)))+ #' from constructor already contains all necessary arguments to pull data. One can try |
||
19 | +98 |
- #'+ #' to execute `pull_callable` directly by `x$pull_callable$run()` or to get code using |
||
20 | +99 |
- #' x <- dataset_connector(+ #' `x$pull_callable$get_code()`. `args` specified in pull are used temporary to get data but |
||
21 | +100 |
- #' pull_callable = fun,+ #' not saved in code. |
||
22 | +101 |
- #' dataname = "ADSL",+ #' @param try (`logical` value)\cr |
||
23 | +102 |
- #' label = "My custom label"+ #' whether perform function evaluation inside `try` clause |
||
24 | +103 |
- #' )+ #' |
||
25 | +104 |
- #' get_dataset_label(x)+ #' @return `self` invisibly for chaining. |
||
26 | +105 |
- get_dataset_label.TealDatasetConnector <- function(x) { # nolint+ pull = function(args = NULL, try = FALSE) { |
||
27 | -17x | +106 | +28x |
- return(x$get_dataset_label())+ logger::log_trace("CDISCTealDatasetConnector$pull pulling dataset: { deparse1(self$get_dataname()) }.") |
28 | -+ | |||
107 | +28x |
- }+ super$pull(args = args, try = try) |
||
29 | +108 | |||
30 | -+ | |||
109 | +27x |
- #' @rdname get_dataset_label+ if (!self$is_failed()) { |
||
31 | -+ | |||
110 | +27x |
- #' @export+ private$dataset <- as_cdisc( |
||
32 | -+ | |||
111 | +27x |
- #' @examples+ private$dataset, |
||
33 | -+ | |||
112 | +27x |
- #' ADSL <- example_cdisc_data("ADSL")+ parent = self$get_parent() |
||
34 | +113 |
- #' ADSL_dataset <- dataset(dataname = "ADSL", x = ADSL, label = "My custom label")+ )+ |
+ ||
114 | +27x | +
+ logger::log_trace("CDISCTealDatasetConnector$pull pulled dataset: { deparse1(self$get_dataname()) }.") |
||
35 | +115 |
- #' get_dataset_label(ADSL_dataset)+ } else {+ |
+ ||
116 | +! | +
+ logger::log_error("CDISCTealDatasetConnector$pull failed to pull dataset: { deparse1(self$get_dataname()) }.") |
||
36 | +117 |
- get_dataset_label.TealDataset <- function(x) {+ } |
||
37 | -31x | +118 | +27x |
- return(x$get_dataset_label())+ return(invisible(self)) |
38 | +119 |
- }+ } |
1 | +120 |
- .onLoad <- function(libname, pkgname) { # nolint+ ), |
|
2 | +121 |
- # expose default CDISC dataset names+ |
|
3 | +122 |
- # copy from excel file+ ## __Private Fields ==== |
|
4 | -! | +||
123 | +
- default_cdisc_keys <- yaml::yaml.load_file(+ private = list( |
||
5 | -! | +||
124 | +
- get_package_file("teal.data", "cdisc_datasets/cdisc_datasets.yaml")+ parent = character(0), |
||
6 | -! | +||
125 | +
- ) # nolint+ |
||
7 | -! | +||
126 | +
- assign("default_cdisc_keys", default_cdisc_keys, envir = parent.env(environment()))+ ## __Private Methods ==== |
||
8 | +127 |
-
+ set_parent = function(parent) { |
|
9 | -+ | ||
128 | +38x |
- # Set up the teal logger instance+ checkmate::assert_character(parent, max.len = 1, any.missing = FALSE) |
|
10 | -! | +||
129 | +38x |
- teal.logger::register_logger("teal.data")+ private$parent <- parent+ |
+ |
130 | +38x | +
+ return(invisible(self)) |
|
11 | +131 |
-
+ } |
|
12 | -! | +||
132 | +
- invisible()+ ) |
||
13 | +133 |
- }+ ) |
1 |
- #' Is pulled+ setOldClass("JoinKeys") |
||
2 |
- #'+ |
||
3 |
- #' @description `r lifecycle::badge("stable")`+ #' Reproducible data class. |
||
4 |
- #' S3 method to determine if dataset is pulled (loaded).+ #' |
||
5 |
- #'+ #' Reproducible data class basing on [`teal.code::qenv-class`]. |
||
6 |
- #' @param x ([`TealDatasetConnector`], [`TealDataset`] or [`TealDataAbstract`])+ #' Don't interact with slots directly, it is suggested to use methods instead |
||
7 |
- #'+ #' (see `methods(class = "teal_data")`). |
||
8 |
- #' @return (`logical`) `TRUE` if connector has been already pulled, else `FALSE`.+ #' |
||
9 |
- #' @export+ #' @name teal_data-class |
||
10 |
- is_pulled <- function(x) {+ #' @rdname teal_data-class |
||
11 | -245x | +
- UseMethod("is_pulled")+ #' |
|
12 |
- }+ #' @slot code (`character`) representing code necessary to reproduce the environment. |
||
13 |
-
+ #' To extract the `code` please use [get_code()]. |
||
14 |
- #' @rdname is_pulled+ #' @slot env (`environment`) environment which content was generated by the evaluation |
||
15 |
- #' @export+ #' of the `code` slot. To extract variables from the environment please use [get_var()] or [`[[`]. |
||
16 |
- #'+ #' @slot id (`integer`) random identifier of the code element to make sure uniqueness |
||
17 |
- #' @examples+ #' when joining. |
||
18 |
- #' # TealDatasetConnector --------+ #' @slot warnings (`character`) the warnings output when evaluating the code. |
||
19 |
- #' library(magrittr)+ #' To extract the `warnings` use [get_warnings()]. |
||
20 |
- #' pull_fun_adsl <- callable_function(teal.data::example_cdisc_data) %>%+ #' @slot messages (`character`) the messages output when evaluating the code |
||
21 |
- #' set_args(list(dataname = "ADSL"))+ #' @slot join_keys (`JoinKeys`) object. |
||
22 |
- #' x <- dataset_connector("ADSL", pull_fun_adsl)+ #' To extract the `join_keys` use [get_join_keys()]. |
||
23 |
- #'+ #' @slot datanames (`character`) names of datasets in `env`. Needed when non-dataset |
||
24 |
- #' is_pulled(x)+ #' objects are needed in the `env` slot. |
||
25 |
- #'+ #' To extract the `datanames` use [get_dataname()]. |
||
26 |
- #' load_dataset(x)+ #' |
||
27 |
- #' is_pulled(x)+ #' @import teal.code |
||
28 |
- is_pulled.TealDatasetConnector <- function(x) {+ #' @keywords internal |
||
29 | -59x | +
- return(x$is_pulled())+ setClass( |
|
30 |
- }+ Class = "teal_data", |
||
31 |
-
+ contains = "qenv", |
||
32 |
- #' @rdname is_pulled+ slots = c(join_keys = "JoinKeys", datanames = "character"), |
||
33 |
- #' @export+ prototype = list( |
||
34 |
- #'+ join_keys = join_keys(), |
||
35 |
- #' @examples+ datanames = character(0) |
||
36 |
- #' # TealDataset --------+ ) |
||
37 |
- #' x <- dataset(+ ) |
||
38 |
- #' dataname = "XY",+ |
||
39 |
- #' x = data.frame(x = c(1, 2), y = c("a", "b"), stringsAsFactors = FALSE),+ #' Initialize `teal_data` object |
||
40 |
- #' keys = "y",+ #' |
||
41 |
- #' code = "XY <- data.frame(x = c(1, 2), y = c('aa', 'bb'),+ #' Initialize `teal_data` object. |
||
42 |
- #' stringsAsFactors = FALSE)"+ #' @name new_teal_data |
||
43 |
- #' )+ #' |
||
44 |
- #'+ #' @param data (`named list`) List of data. |
||
45 |
- #' is_pulled(x)+ #' @param code (`character` or `language`) code to reproduce the `data`. |
||
46 |
- is_pulled.TealDataset <- function(x) {+ #' Accepts and stores comments also. |
||
47 | -173x | +
- return(x$is_pulled())+ #' @param keys (`JoinKeys`) object |
|
48 |
- }+ #' @param datanames (`character`) names of datasets passed to `data`. |
||
49 |
-
+ #' Needed when non-dataset objects are needed in the `env` slot. |
||
50 |
- #' @rdname is_pulled+ #' @rdname new_teal_data |
||
51 |
- #' @export+ #' @keywords internal |
||
52 |
- #'+ new_teal_data <- function(data, code = character(0), keys = join_keys(), datanames = names(data)) { |
||
53 | -+ | 12x |
- #' @examples+ checkmate::assert_list(data) |
54 | -+ | 12x |
- #'+ checkmate::assert_class(keys, "JoinKeys") |
55 | +12x | +
+ checkmate::assert_character(datanames)+ |
+ |
56 | +12x | +
+ if (!any(is.language(code), is.character(code))) {+ |
+ |
57 | +! | +
+ stop("`code` must be a character or language object.")+ |
+ |
58 |
- #' library(magrittr)+ }+ |
+ ||
59 | +12x | +
+ code <- format_expression(code) |
|
56 | +60 |
- #'+ + |
+ |
61 | +12x | +
+ new_env <- rlang::env_clone(list2env(data), parent = parent.env(.GlobalEnv))+ |
+ |
62 | +12x | +
+ lockEnvironment(new_env, bindings = TRUE) |
|
57 | +63 |
- #' # TealData --------+ + |
+ |
64 | +12x | +
+ methods::new(+ |
+ |
65 | +12x | +
+ "teal_data",+ |
+ |
66 | +12x | +
+ env = new_env,+ |
+ |
67 | +12x | +
+ code = code,+ |
+ |
68 | +12x | +
+ warnings = "",+ |
+ |
69 | +12x | +
+ messages = "",+ |
+ |
70 | +12x | +
+ id = sample.int(.Machine$integer.max, size = 1L),+ |
+ |
71 | +12x | +
+ join_keys = keys,+ |
+ |
72 | +12x | +
+ datanames = datanames |
|
58 | +73 |
- #' x1 <- dataset(+ ) |
|
59 | +74 |
- #' x = teal.data::example_cdisc_data("ADSL"),+ } |
60 | +1 |
- #' dataname = "ADSL",+ .onLoad <- function(libname, pkgname) { # nolint |
|
61 | +2 |
- #' keys = get_cdisc_keys("ADSL"),+ # expose default CDISC dataset names |
|
62 | +3 |
- #' code = "ADSL <- teal.data::example_cdisc_data(\"ADSL\")",+ # copy from excel file+ |
+ |
4 | +! | +
+ default_cdisc_keys <- yaml::yaml.load_file(+ |
+ |
5 | +! | +
+ get_package_file("teal.data", "cdisc_datasets/cdisc_datasets.yaml")+ |
+ |
6 | +! | +
+ ) # nolint+ |
+ |
7 | +! | +
+ assign("default_cdisc_keys", default_cdisc_keys, envir = parent.env(environment())) |
|
63 | +8 |
- #' label = "ADTTE dataset"+ |
|
64 | +9 |
- #' )+ # Set up the teal logger instance+ |
+ |
10 | +! | +
+ teal.logger::register_logger("teal.data") |
|
65 | +11 |
- #'+ + |
+ |
12 | +! | +
+ invisible() |
|
66 | +13 |
- #' x2 <- dataset(+ } |
67 | +1 |
- #' x = teal.data::example_cdisc_data("ADTTE"),+ #' Get dataset attributes |
|
68 | +2 |
- #' dataname = "ADTTE",+ #' |
|
69 | +3 |
- #' keys = get_cdisc_keys("ADTTE"),+ #' @description `r lifecycle::badge("stable")` |
|
70 | +4 |
- #' code = "ADTTE <- teal.data::example_cdisc_data(\"ADTTE\")",+ #' Get dataset attributes in form of named list. |
|
71 | +5 |
- #' label = "ADTTE dataset"+ #' |
|
72 | +6 |
- #' )+ #' @param x an object of (`TealDataset`) class |
|
73 | +7 |
#' |
|
74 | +8 |
- #' rd <- teal_data(x1, x2)+ #' @return named `list` of object attributes |
|
75 | +9 |
- #' is_pulled(rd)+ #' |
|
76 | +10 |
- #'+ #' @export |
|
77 | +11 |
- #' # TealDataConnector --------+ get_attrs <- function(x) {+ |
+ |
12 | +! | +
+ UseMethod("get_attrs") |
|
78 | +13 |
- #' adsl_cf <- callable_function(teal.data::example_cdisc_data) %>%+ } |
|
79 | +14 |
- #' set_args(list(dataname = "ADSL"))+ |
|
80 | +15 |
- #' adsl <- cdisc_dataset_connector(+ |
|
81 | +16 |
- #' dataname = "ADSL",+ #' @rdname get_attrs |
|
82 | +17 |
- #' pull_callable = adsl_cf,+ #' @export |
|
83 | +18 |
- #' keys = get_cdisc_keys("ADSL")+ #' @examples |
|
84 | +19 |
- #' )+ #' # TealDataset -------- |
|
85 | +20 |
#' |
|
86 | +21 |
- #' new_cf <- callable_function(function(x) {+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
|
87 | +22 |
- #' x$NEW <- 1:nrow(x)+ #' |
|
88 | +23 |
- #' x+ #' x1 <- dataset("ADSL", x = ADSL, label = "custom label") |
|
89 | +24 |
- #' })+ #' get_attrs(x1) |
|
90 | +25 |
- #' new_cf$set_args(list(x = as.name("x")))+ #' |
|
91 | +26 |
- #' new <- cdisc_dataset_connector(+ #' x2 <- dataset( |
|
92 | +27 |
- #' dataname = "NEW",+ #' "ADSL", |
|
93 | +28 |
- #' pull_callable = new_cf,+ #' x = ADSL, |
|
94 | +29 |
#' keys = get_cdisc_keys("ADSL"), |
|
95 | +30 |
- #' vars = list(x = adsl)+ #' label = "custom label" |
|
96 | +31 |
#' ) |
|
97 | +32 | ++ |
+ #' get_attrs(x2)+ |
+
33 |
#' |
||
98 | +34 |
- #' rdc <- cdisc_data(adsl, new)+ #' # CDISCTealDataset -------- |
|
99 | +35 |
#' |
|
100 | +36 |
- #' is_pulled(rdc)+ #' ADSL <- teal.data::example_cdisc_data("ADSL") |
|
101 | +37 |
- #' \dontrun{+ #' x3 <- cdisc_dataset( |
|
102 | +38 |
- #' load_datasets(rdc)+ #' "ADSL", |
|
103 | +39 |
- #' is_pulled(rdc)+ #' x = ADSL, |
|
104 | +40 |
- #' }+ #' keys = get_cdisc_keys("ADSL"), |
|
105 | +41 |
- is_pulled.TealDataAbstract <- function(x) { # nolint+ #' label = "custom label" |
|
106 | -13x | +||
42 | +
- return(x$is_pulled())+ #' ) |
||
107 | +43 | ++ |
+ #' get_attrs(x3)+ |
+
44 | ++ |
+ get_attrs.TealDataset <- function(x) {+ |
+ |
45 | +! | +
+ return(x$get_attrs())+ |
+ |
46 |
} |