Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add stack validation utils #427

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ S3method(input_ids,hidden_field)
S3method(input_ids,list_field)
S3method(is_initialized,block)
S3method(is_initialized,field)
S3method(is_valid,block)
S3method(is_valid,field)
S3method(is_valid,stack)
S3method(layout,block)
S3method(remove_button,block)
S3method(remove_button,stack)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# blockr 0.0.2.9031

## Feature
- New validate stack function.
- Improved `submit` feature for blocks. Now submit isn't added as a class but as a special block attribute. When you design a block, you can pass the `submit` parameter like so:

```r
Expand Down
158 changes: 125 additions & 33 deletions R/server.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@
#' @rdname generate_server
#' @export
generate_server.result_field <- function(x, ...) {
function(id, init = NULL, data = NULL) {
function(id, init = NULL, data = NULL, is_link_valid = NULL) {
moduleServer(id, function(input, output, session) {
get_result <- function(inp) {
req(inp)
res <- get_stack_result(
get_workspace_stack(inp)
)
get_result <- function(inp = NULL) {
if (is.null(inp)) {
res <- data.frame()
# Needed by ui_update
attr(res, "result_field_stack_name") <- ""
return(res)
}
stack <- workspace_stacks()[[inp]]$stack
res <- get_stack_result(stack)

attr(res, "result_field_stack_name") <- inp

Expand All @@ -44,13 +48,17 @@
updateSelectInput(
session,
"select-stack",
choices = result_field_stack_opts(session$ns, workspace_stacks()),
choices = result_field_stack_opts(session$ns, names(workspace_stacks())),
selected = input[["select-stack"]]
)
)

reactive({
get_result(input[["select-stack"]])
if (is_link_valid()) {
get_result(input[["select-stack"]])
} else {
get_result()
}
})
})
}
Expand Down Expand Up @@ -82,19 +90,21 @@
}
}

generate_server_block <- function(

Check warning on line 93 in R/server.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/server.R,line=93,col=1,[cyclocomp_linter] Functions should have cyclomatic complexity of less than 15, this has 16.
x,
in_dat = NULL,
id,
display = c("table", "plot"),
is_prev_valid) {
is_prev_valid, linked_stack = NULL) {
display <- match.arg(display)

# if in_dat is NULL (data block), turn it into a reactive expression that
# returns NULL
if (is.null(in_dat)) {
in_dat <- reactive(NULL)
is_prev_valid <- reactive(NULL)
} else {
linked_stack <- reactive(NULL)
}

obs_expr <- function(x) {
Expand Down Expand Up @@ -136,12 +146,16 @@
l_values_module <- list() # a list with reactive values (module server output)
for (name in names(x_srv)) {
l_values_module[[name]] <-
generate_server(x_srv[[name]])(name, init = l_init[[name]], data = in_dat)
generate_server(x_srv[[name]])(
name,
init = l_init[[name]],
data = in_dat,

Check warning on line 152 in R/server.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/server.R,line=152,col=27,[trailing_whitespace_linter] Trailing whitespace is superfluous.
is_link_valid = reactive(linked_stack()$is_valid)
)
}

# proceed in standard fashion (if fields have no generate_server)
r_values_default <- reactive({
# if (!is.null(is_prev_valid)) req(is_prev_valid)
blk_no_srv <- blk()
blk_no_srv[is_srv] <- NULL # to keep class etc
eval(obs_expr(blk_no_srv))
Expand All @@ -156,7 +170,7 @@

# This will also trigger when the previous block
# valid status changes.
obs$update_blk <- observeEvent(c(r_values(), in_dat(), is_prev_valid()),
obs$update_blk <- observeEvent(c(r_values(), in_dat(), linked_stack()$is_valid),
{
# 1. upd blk,
b <- update_blk(
Expand All @@ -174,7 +188,7 @@
log_debug("Updating UI of block ", class(x)[[1]])

# Validating
is_valid$block <- validate_block(blk())
is_valid$block <- is_valid(blk())
is_valid$message <- attr(is_valid$block, "msg")
is_valid$fields <- attr(is_valid$block, "fields")
log_debug("Validating block ", class(x)[[1]])
Expand Down Expand Up @@ -210,7 +224,7 @@
out_dat <- if (attr(x, "submit") > -1) {
eventReactive(input$submit,
{
req(is_valid$block)
if (!is_valid$block) return(data.frame())
if (is.null(in_dat())) {
evaluate_block(blk())
} else {
Expand All @@ -223,7 +237,7 @@
)
} else {
reactive({
req(is_valid$block)
if (!is_valid$block) return(data.frame())
if (is.null(in_dat()) && !inherits(x, "transform_block")) {
evaluate_block(blk())
} else {
Expand Down Expand Up @@ -291,8 +305,8 @@

#' @rdname generate_server
#' @export
generate_server.data_block <- function(x, id, ...) {
generate_server_block(x = x, in_dat = NULL, id = id, is_prev_valid = NULL)
generate_server.data_block <- function(x, id, linked_stack, ...) {
generate_server_block(x = x, in_dat = NULL, id = id, is_prev_valid = NULL, linked_stack = linked_stack)

Check warning on line 309 in R/server.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/server.R,line=309,col=101,[line_length_linter] Lines should not be more than 100 characters. This line is 105 characters.
}

#' @param in_dat Reactive input data
Expand Down Expand Up @@ -322,7 +336,7 @@
#' @rdname generate_server
#' @export
generate_server.stack <- function(x, id = NULL, new_block = NULL,
workspace = get_workspace(), ...) {
workspace = get_workspace(), prev_stack, ...) {
stopifnot(...length() == 0L)

id <- coal(id, get_stack_name(x))
Expand All @@ -339,10 +353,14 @@
moduleServer(
id = id,
function(input, output, session) {
ns <- session$ns

vals <- reactiveValues(
stack = x,
blocks = vector("list", length(x)),
removed = FALSE
removed = FALSE,
is_valid = NULL,
prev_stack = NULL
)
# Don't remove: needed by shinytest2
exportTestValues(
Expand Down Expand Up @@ -396,19 +414,70 @@
# Any block change: data or input should be sent
# up to the stack so we can properly serialise.
observeEvent(
c(
get_block_vals(vals$blocks),
get_last_block_data(vals$blocks)()
),
{
vals$stack <- set_stack_blocks(
vals$stack,
get_block_vals(vals$blocks),
get_last_block_data(vals$blocks)
req(length(vals$blocks) > 0)
c(
lapply(vals$blocks, \(block) {
block$is_valid()
}),
get_block_vals(vals$blocks)
)
}
},
{
# get_last_block_data(vals$blocks)() errors
# if any block is invalid
tryCatch(
{
vals$stack <- set_stack_blocks(
vals$stack,
get_block_vals(vals$blocks),
get_last_block_data(vals$blocks)()
)
},
error = function(e) {
vals$stack <- set_stack_blocks(
vals$stack,
get_block_vals(vals$blocks),
list()
)
}
)
vals$is_valid <- is_valid(vals$stack)
}, priority = 1000
)

# stack UI validation message
# We only display which block is invalid
observeEvent(c(vals$is_valid, prev_stack()$is_valid), {
vals$prev_stack <- prev_stack()
removeUI(sprintf("#%s .stack-validation-message", ns(NULL)))
msg <- if (is.null(prev_stack()$is_valid)) {
HTML(paste(
lapply(attr(vals$is_valid, "msgs"), \(msg) {
sprintf("Block %s is invalid", msg)
}),
collapse = ", </br>"
))
} else {
if (!prev_stack()$is_valid) {
"Linked stack isn't valid. Please fix upstream errors."
}
}
insertUI(
sprintf("#%s .stack-validation", ns(NULL)),
ui = div(
class = "text-danger text-center stack-validation-message",
msg
)
)

# Disable copy code button
session$sendCustomMessage(
"toggle-copy-code",
list(state = vals$is_valid, id = ns("copy"))
)
})

observeEvent(vals$stack, {
log_debug("UPDADING WORKSPACE with stack ", id)
add_workspace_stack(id, vals$stack,
Expand All @@ -424,7 +493,7 @@
session$sendCustomMessage(
"blockr-render-stack",
list(
stack = session$ns(NULL),
stack = ns(NULL),
locked = is_locked(session)
)
)
Expand Down Expand Up @@ -687,7 +756,12 @@
vals$stacks[[stack_id]] <- generate_server(
el,
id = stack_id,
new_block = reactive(vals$new_block[[stack_id]])
new_block = reactive(vals$new_block[[stack_id]]),
prev_stack = if (length(vals$stacks) == 1) {
reactive(NULL)
} else {
reactive(vals$stacks[[length(vals$stacks) - 1]])
}
)

# Handle new block injection
Expand All @@ -702,9 +776,16 @@
})

attr(x, "reactive_stack_directory") <- reactive({
names(vals$stacks)
vals$stacks
}) |> bindEvent(
chr_ply(lapply(vals$stacks, `[[`, "stack"), attr, "title")
c(
chr_ply(lapply(vals$stacks, `[[`, "stack"), attr, "title"),
lapply(vals$stacks, \(stack) {
lgl_ply(stack$blocks, \(block) {
block$is_valid()
})
})
)
)

# Serialize
Expand Down Expand Up @@ -741,10 +822,16 @@

observeEvent(TRUE, {
lapply(names(stacks), \(nme) {
idx <- which(nme == names(stacks))
vals$stacks[[nme]] <- generate_server(
stacks[[nme]],
id = nme,
new_block = reactive(vals$new_block[[nme]])
new_block = reactive(vals$new_block[[nme]]),
prev_stack = if (idx == 1) {
reactive(NULL)
} else {
reactive(vals$stacks[[nme]])
}
)
})
})
Expand Down Expand Up @@ -813,6 +900,11 @@
NULL
} else {
vals$blocks[[i - 1]]$is_valid
},
linked_stack = if (i == 1) {
reactive(vals$prev_stack)
} else {
NULL
}
)
}
Expand Down
1 change: 1 addition & 0 deletions R/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ generate_ui.stack <- function(x, id = NULL, ...) {
class = "card stack border",
id = id,
stack_header(x, id, ns),
div(class = "stack-validation"),
div(
class = "card-body p-1",
id = sprintf("%s-body", id),
Expand Down
2 changes: 1 addition & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ send_error_to_ui <- function(blk, is_valid, session) {
)

# Toggle submit field
if (!is.null(attr(blk, "submit"))) {
if (attr(blk, "submit") > -1) {
session$sendCustomMessage(
"toggle-submit",
list(state = is_valid$block, id = ns("submit"))
Expand Down
20 changes: 17 additions & 3 deletions R/validation.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ is_valid.field <- function(x) {
)
}

validate_block <- function(x) {
tmp <- lapply(names(x), \(name) {
is_valid(x[[name]])
#' @rdname validate_field
#' @export
is_valid.block <- function(x) {
tmp <- lapply(x, \(field) {
is_valid(field)
})
structure(
all(unlist(tmp) == TRUE),
Expand All @@ -54,6 +56,18 @@ validate_block <- function(x) {
)
}

#' @rdname validate_field
#' @export
is_valid.stack <- function(x) {
tmp <- lapply(x, \(block) {
is_valid(block)
})
structure(
all(unlist(tmp) == TRUE),
msgs = which(tmp == FALSE) # captures invalid blocks
)
}

#' @param ... Message components (forwarded to [paste0()])
#' @param class Condition class (will be a subclass of `validation_failure`)
#' @rdname validate_field
Expand Down
2 changes: 1 addition & 1 deletion inst/assets/index.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion man/generate_server.Rd

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

Loading
Loading