From b0cc00b6cd6b733dc05e4a435922f187be90d491 Mon Sep 17 00:00:00 2001 From: DivadNojnarg Date: Mon, 16 Sep 2024 15:44:41 +0200 Subject: [PATCH 1/5] blockr.data is a suggest + update remote url --- DESCRIPTION | 6 +++--- NAMESPACE | 1 - R/block-core.R | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f00e8569..ed48369d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,7 +32,6 @@ Imports: bslib, methods, DT, - blockr.data, htmltools, shinyAce, jsonlite, @@ -42,8 +41,8 @@ Imports: rlang, shinyWidgets Remotes: - blockr-org/blockr.data, DivadNojnarg/DiagrammeR + BristolMyersSquibb/blockr.data Suggests: knitr, rmarkdown, @@ -55,6 +54,7 @@ Suggests: withr, ggplot2, vdiffr, - palmerpenguins + palmerpenguins, + blockr.data Config/testthat/edition: 3 VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index 2e77fa3a..0fa3b142 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -266,7 +266,6 @@ export(validation_failure) export(value) export(values) export(write_log) -import(blockr.data) import(dplyr) import(shiny) importFrom(htmltools,htmlDependency) diff --git a/R/block-core.R b/R/block-core.R index 8d078a41..076db923 100644 --- a/R/block-core.R +++ b/R/block-core.R @@ -13,7 +13,6 @@ #' @param class Block subclass #' #' @export -#' @import blockr.data #' @import dplyr #' @importFrom stats setNames new_block <- function(fields, expr, name = rand_names(), ..., From 87b5b5c8720224a33936b4e949addd6c08abfb89 Mon Sep 17 00:00:00 2001 From: DivadNojnarg Date: Mon, 16 Sep 2024 17:47:45 +0200 Subject: [PATCH 2/5] also start improve shinylive links management --- DESCRIPTION | 2 + NAMESPACE | 1 - R/sysdata.rda | Bin 0 -> 1056 bytes R/utils.R | 26 +++-- README.Rmd | 2 +- index.Rmd | 100 +++++++------------ index.md | 116 ++++++++++++---------- inst/shinylive/apps/palmer-penguins/app.R | 47 +++++++++ inst/shinylive/tools.R | 22 ++++ man/create_app_link.Rd | 6 +- man/figures/README-unnamed-chunk-2-1.png | Bin 18748 -> 33487 bytes man/figures/README-unnamed-chunk-4-1.png | Bin 0 -> 33487 bytes vignettes/blockr.Rmd | 2 +- 13 files changed, 191 insertions(+), 133 deletions(-) create mode 100644 R/sysdata.rda create mode 100644 inst/shinylive/apps/palmer-penguins/app.R create mode 100644 inst/shinylive/tools.R create mode 100644 man/figures/README-unnamed-chunk-4-1.png diff --git a/DESCRIPTION b/DESCRIPTION index ed48369d..d9643d87 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -58,3 +58,5 @@ Suggests: blockr.data Config/testthat/edition: 3 VignetteBuilder: knitr +Depends: + R (>= 2.10) diff --git a/NAMESPACE b/NAMESPACE index 0fa3b142..56a41f8a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -152,7 +152,6 @@ export(cat_logger) export(clear_workspace) export(cnd_logger) export(construct_block) -export(create_app_link) export(create_block) export(data_info) export(download_ui) diff --git a/R/sysdata.rda b/R/sysdata.rda new file mode 100644 index 0000000000000000000000000000000000000000..bdcc9d6575ea5dfd371e30948e6bdc9ccc7ff39f GIT binary patch literal 1056 zcmV+*1mF8YT4*^jL0KkKSz0ku0ssLge}MilzyJsf|MEZo|M0*6|L_0-Km#2+I6CIt zQ@F~0lW3=j57DBYk?3it^)&GrPbQjrXcXE{QMDP745|K_CYY)CXb{m)z|bdOD^qW}pB7A`>PFPZaV!CR5UBSi7=Q)?5ePv5wA(kZ;qkkDT_)-xx66P% z!OOlne|(>lq=+amSUD)C9Cb5#4VLTYu3=Yd2Cr`XErY;9b(=GFQ9)gs0CU4cb{@*T z$bsRQnlQK!WBxS}%*_duaXhcbjD}XpB=a|E<73ho-HsE4yt-V0sTnW5JkPZ_VFsXF zGvLQ(RvuK!x!uhC&be=&S6k02@*FF!o=TO*hW08cNZB+lIDTr&MV6eW9otvnI#Wqy zgx!X+cJ%9OG*=t$bBp_0lRgFyQSJWN9}e$-qi3Fb-LDuX^dHu9QnVy=$@mST?n|lG5#Z7H`q=ROB=?5+r=ZqaxbRT~eHH7e`i*&yqGfLE&p} zvYQ#D*s9K+`{Bii@?fe#lPq5kyYNOjIfZ;LZ@=cQHjM%3z1;0kBy{}C=_q}3y>5Jth~50$IuX$W?$r6=K(P{82zFk> zaTc{Grr!GMyfn(_mhTu_eQb8#*`BCPPq6$(uTlW-jmzO#MS;^}ho~ne#eR{E literal 0 HcmV?d00001 diff --git a/R/utils.R b/R/utils.R index 94f07c36..b156b116 100644 --- a/R/utils.R +++ b/R/utils.R @@ -322,19 +322,16 @@ has_method <- function(x, generic) { #' Create shinylive iframe #' -#' @param app_code base64 app code. You can create it from https://shinylive.io/r -#' by writing code and click on share and copy the link. The code is located at -#' the end of the url. +#' Useful for pkgdown website +#' +#' @param url app url. A shinylive link. #' @param mode How to display the shinylive app. Default to app mode. #' @param header Whether to display the shinylive header. Default to TRUE. -#' -#' @export -create_app_link <- function(app_code, mode = c("app", "editor"), header = TRUE) { +#' @keywords internal +create_app_link <- function(url, mode = c("app", "editor"), header = TRUE) { mode <- match.arg(mode) - app_url <- sprintf( - "https://shinylive.io/r/%s/#code=%s", mode, app_code - ) + if (mode != "editor") app_url <- gsub("editor", mode, url) if (!header) { app_url <- paste0(app_url, "&h=0") @@ -353,6 +350,17 @@ create_app_link <- function(app_code, mode = c("app", "editor"), header = TRUE) ) } +code_chunk <- function(output, language = "r") { + cat(paste0("```", language)) + cat(output) + cat("\n```\n") +} + +print_shinylive_r_code <- function(name) { + path <- sprintf("inst/shinylive/apps/%s/app.R", name) + code_chunk(cat(paste(readLines(path)[-1], collapse = "\n"))) +} + get_block_title <- function(x) { registry <- available_blocks() block <- registry[sapply(registry, \(blk) { diff --git a/README.Rmd b/README.Rmd index 62cb16ae..a7cacafa 100644 --- a/README.Rmd +++ b/README.Rmd @@ -84,7 +84,7 @@ To get a better idea of `{blockr}` capabilities in various data context, you can You can install the development version of blockr from [GitHub](https://github.com/) with: ```r -pak::pak("blockr-org/blockr") +pak::pak("BristolMyersSquibb/blockr") ``` ## Example: palmer penguins case study diff --git a/index.Rmd b/index.Rmd index 6444deb2..5ee9bb28 100644 --- a/index.Rmd +++ b/index.Rmd @@ -30,7 +30,39 @@ library(DiagrammeR) ## Why blockr? -`{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and __code-free__ approach to building data pipelines. It allows users to create __powerful__ data workflows using pre-built __blocks__ that can be easily __connected__, all without writing a single line of code. +`{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and __code-free__ approach to building data pipelines. + +## Who is it for? + +`{blockr}` has 2 main user targets: + +1. On the one hand, it empowers __non technical__ users to create insightful data workflows using pre-built blocks that can be __easily__ connected, all without writing a single line of code. + +Below is a simple pre-built case study involving `{blockr}`. We use the palmerpenguins dataset to find out which femal species has the largest flippers. This tiny dashboard is composed of 4 steps: import the data, filter, create the plot and chose the geometry (points). Within each step (block), the user can change inputs and see the changes propagate in real time. Notice that the filter step requires to press a submit button before moving forward, which prevents the plot from appearing first. This is to prevent long running task from being run unecessarily. You can find more in other vignettes. + +```{r, echo=FALSE} +# shinylive container +card( + blockr:::create_app_link( + blockr:::shinylive_links["palmer-penguins"], + "app", + header = FALSE + ), + full_screen = TRUE +) +``` + +You can of course start with a totally empty dashboard and create your own analysis from scratch. + +2. On the other hand, it provides __developers__ with a set of tools to seamlessly create new blocks, thereby enhancing the entire framework and fostering __collaboration__ within organizations teams. For instance, regarding the previous example, below is what it takes to create such dashboard. + +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +print_shinylive_r_code("palmer-penguins") +``` + +Note that the `{blockr.ggplot2}` [package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes some ready to use blocks as shown above. + +## blockr at a glance ```{r, echo=FALSE, message=FALSE} mermaid(" @@ -86,71 +118,7 @@ To get a better idea of `{blockr}` capabilities in various data context, you can You can install the development version of blockr from [GitHub](https://github.com/) with: ```r -pak::pak("blockr-org/blockr") -``` - -## Example: palmer penguins case study - -Below is a simple case study involving `{blockr}`. We use the palmerpenguins dataset to find out which -femal species has the largest flippers. We create 2 custom blocks allowing to create our plot block (see the plot vignette for more details). Note that the `{blockr.ggplot2}` package exposes some ready to use blocks. - -
-
- -
- - - - - -
- -```{r, eval=FALSE} -library(blockr) -library(palmerpenguins) -library(ggplot2) - -new_ggplot_block <- function(col_x = character(), col_y = character(), ...) { - - data_cols <- function(data) colnames(data) - - new_block( - fields = list( - x = new_select_field(col_x, data_cols, type = "name"), - y = new_select_field(col_y, data_cols, type = "name") - ), - expr = quote( - ggplot(mapping = aes(x = .(x), y = .(y))) - ), - class = c("ggplot_block", "plot_block"), - ... - ) -} - -new_geompoint_block <- function(color = character(), shape = character(), ...) { - - data_cols <- function(data) colnames(data$data) - - new_block( - fields = list( - color = new_select_field(color, data_cols, type = "name"), - shape = new_select_field(shape, data_cols, type = "name") - ), - expr = quote( - geom_point(aes(color = .(color), shape = .(shape)), size = 2) - ), - class = c("plot_layer_block", "plot_block"), - ... - ) -} - -stack <- new_stack( - data_block = new_dataset_block("penguins", "palmerpenguins"), - filter_block = new_filter_block("sex", "female"), - plot_block = new_ggplot_block("flipper_length_mm", "body_mass_g"), - layer_block = new_geompoint_block("species", "species") -) -serve_stack(stack) +pak::pak("BristolMyersSquibb/blockr") ``` ## Contribute diff --git a/index.md b/index.md index 688c4d74..f0ad812f 100644 --- a/index.md +++ b/index.md @@ -15,69 +15,46 @@ `{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and **code-free** approach to building -data pipelines. It allows users to create **powerful** data workflows -using pre-built **blocks** that can be easily **connected**, all without -writing a single line of code. +data pipelines. -![](man/figures/README-unnamed-chunk-2-1.png) +## Who is it for? -To get started, we invite you to read this -[vignette](https://blockr-org.github.io/blockr/articles/blockr.html). +`{blockr}` has 2 main user targets: -To get a better idea of `{blockr}` capabilities in various data context, -you can look at this -[vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). +1. On the one hand, it empowers **non technical** users to create + insightful data workflows using pre-built blocks that can be + **easily** connected, all without writing a single line of code. -## Key features - -1. **User-Friendly Interface**: Build data pipelines with intuitive - interface. -2. **Flexibility**: Easily add, remove, or rearrange blocks in your - pipeline. -3. **Extensibility**: Developers can create custom blocks to extend - functionality. -4. **Reproducibility**: Pipelines created with `blockr` are easily - shareable and reproducible, with exportable code. -5. **Interactivity**: Real-time feedback as you build and modify your - pipeline. - -## Installation - -You can install the development version of blockr from -[GitHub](https://github.com/) with: - -``` r -pak::pak("blockr-org/blockr") -``` - -## Example: palmer penguins case study - -Below is a simple case study involving `{blockr}`. We use the +Below is a simple pre-built case study involving `{blockr}`. We use the palmerpenguins dataset to find out which femal species has the largest -flippers. We create 2 custom blocks allowing to create our plot block -(see the plot vignette for more details). Note that the -`{blockr.ggplot2}` package exposes some ready to use blocks. - -
- -
- - - +flippers. This tiny dashboard is composed of 4 steps: import the data, +filter, create the plot and chose the geometry (points). Within each +step (block), the user can change inputs and see the changes propagate +in real time. Notice that the filter step requires to press a submit +button before moving forward, which prevents the plot from appearing +first. This is to prevent long running task from being run unecessarily. +You can find more in other vignettes. + +
+
+
- - + -
+You can of course start with a totally empty dashboard and create your +own analysis from scratch. + +2. On the other hand, it provides **developers** with a set of tools to + seamlessly create new blocks, thereby enhancing the entire framework + and fostering **collaboration** within organizations teams. For + instance, regarding the previous example, below is what it takes to + create such dashboard. + ``` r library(blockr) library(palmerpenguins) @@ -126,6 +103,43 @@ stack <- new_stack( serve_stack(stack) ``` +Note that the `{blockr.ggplot2}` +[package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes +some ready to use blocks as shown above. + +## blockr at a glance + +![](man/figures/README-unnamed-chunk-4-1.png) + +To get started, we invite you to read this +[vignette](https://blockr-org.github.io/blockr/articles/blockr.html). + +To get a better idea of `{blockr}` capabilities in various data context, +you can look at this +[vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). + +## Key features + +1. **User-Friendly Interface**: Build data pipelines with intuitive + interface. +2. **Flexibility**: Easily add, remove, or rearrange blocks in your + pipeline. +3. **Extensibility**: Developers can create custom blocks to extend + functionality. +4. **Reproducibility**: Pipelines created with `blockr` are easily + shareable and reproducible, with exportable code. +5. **Interactivity**: Real-time feedback as you build and modify your + pipeline. + +## Installation + +You can install the development version of blockr from +[GitHub](https://github.com/) with: + +``` r +pak::pak("BristolMyersSquibb/blockr") +``` + ## Contribute Easiest is to run `make`, otherwise: diff --git a/inst/shinylive/apps/palmer-penguins/app.R b/inst/shinylive/apps/palmer-penguins/app.R new file mode 100644 index 00000000..3fdffe4b --- /dev/null +++ b/inst/shinylive/apps/palmer-penguins/app.R @@ -0,0 +1,47 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos/", "https://repo.r-wasm.org")) #nolint + +library(blockr) +library(palmerpenguins) +library(ggplot2) + +new_ggplot_block <- function(col_x = character(), col_y = character(), ...) { + + data_cols <- function(data) colnames(data) + + new_block( + fields = list( + x = new_select_field(col_x, data_cols, type = "name"), + y = new_select_field(col_y, data_cols, type = "name") + ), + expr = quote( + ggplot(mapping = aes(x = .(x), y = .(y))) + ), + class = c("ggplot_block", "plot_block"), + ... + ) +} + +new_geompoint_block <- function(color = character(), shape = character(), ...) { + + data_cols <- function(data) colnames(data$data) + + new_block( + fields = list( + color = new_select_field(color, data_cols, type = "name"), + shape = new_select_field(shape, data_cols, type = "name") + ), + expr = quote( + geom_point(aes(color = .(color), shape = .(shape)), size = 2) + ), + class = c("plot_layer_block", "plot_block"), + ... + ) +} + +stack <- new_stack( + data_block = new_dataset_block("penguins", "palmerpenguins"), + filter_block = new_filter_block("sex", "female"), + plot_block = new_ggplot_block("flipper_length_mm", "body_mass_g"), + layer_block = new_geompoint_block("species", "species") +) +serve_stack(stack) diff --git a/inst/shinylive/tools.R b/inst/shinylive/tools.R new file mode 100644 index 00000000..05a2e9c0 --- /dev/null +++ b/inst/shinylive/tools.R @@ -0,0 +1,22 @@ +pak::pak("parmsam/r-shinylive@feat/encode-decode-url") #nolint start + +create_shinylive_links <- function(path) { + + dirs <- list.dirs(path)[-1] + + vapply( + dirs, + shinylive:::url_encode_dir, + FUN.VALUE = character(1) + ) +} + +create_vignettes_links <- function() { + apps_path <- "inst/shinylive/apps" + links <- create_shinylive_links(apps_path) + names(links) <- gsub(sprintf("%s/", apps_path), "", names(links)) + links +} + +shinylive_links <- create_vignettes_links() +usethis::use_data(shinylive_links, internal = TRUE, overwrite = TRUE) #nolint end \ No newline at end of file diff --git a/man/create_app_link.Rd b/man/create_app_link.Rd index ffcf2611..7eaec273 100644 --- a/man/create_app_link.Rd +++ b/man/create_app_link.Rd @@ -4,12 +4,10 @@ \alias{create_app_link} \title{Create shinylive iframe} \usage{ -create_app_link(app_code, mode = c("app", "editor"), header = TRUE) +create_app_link(url, mode = c("app", "editor"), header = TRUE) } \arguments{ -\item{app_code}{base64 app code. You can create it from https://shinylive.io/r -by writing code and click on share and copy the link. The code is located at -the end of the url.} +\item{url}{app url. A shinylive link.} \item{mode}{How to display the shinylive app. Default to app mode.} diff --git a/man/figures/README-unnamed-chunk-2-1.png b/man/figures/README-unnamed-chunk-2-1.png index 8a47c8974cae0a0dcbf75b0bed024bca80ca31e6..a4809bfee32feaa1cf98af26e311c09169c5e097 100644 GIT binary patch literal 33487 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 Tg0=~ Date: Tue, 17 Sep 2024 15:53:50 +0200 Subject: [PATCH 3/5] update all shinylive examples + refactor doc for non developers --- DESCRIPTION | 4 +- R/sysdata.rda | Bin 1056 -> 12555 bytes R/utils.R | 12 +- README.Rmd | 122 ++----- README.md | 99 +++-- _pkgdown.yml | 30 +- index.Rmd | 45 +-- index.md | 12 +- inst/shinylive/apps/ae-forest-plot/app.R | 159 ++++++++ inst/shinylive/apps/custom-field/app.R | 98 +++++ inst/shinylive/apps/dataset-block/app.R | 16 + inst/shinylive/apps/empty-stack/app.R | 11 + inst/shinylive/apps/filesbrowser-block/app.R | 13 + .../apps/filesbrowser-block/penguins.csv | 345 ++++++++++++++++++ inst/shinylive/apps/ggplot-block/app.R | 46 +++ inst/shinylive/apps/ode-demo/app.R | 51 +++ inst/shinylive/apps/registry-demo/app.R | 29 ++ inst/shinylive/apps/result-block/app.R | 14 + inst/shinylive/apps/upload-block/app.R | 10 + .../apps/workspace-kitchen-sink/app.R | 7 + man/create_app_link.Rd | 3 +- man/figures/README-unnamed-chunk-3-1.png | Bin 0 -> 33487 bytes ...lockr_examples.Rmd => blockr-examples.Rmd} | 231 +----------- vignettes/blockr.Rmd | 222 +++++------ vignettes/data-blocks.Rmd | 79 ++-- .../{developer-guide.Rmd => internals.Rmd} | 4 +- vignettes/new-field.Rmd | 47 +-- vignettes/plot-block.Rmd | 4 +- vignettes/registry.Rmd | 13 +- 29 files changed, 1073 insertions(+), 653 deletions(-) create mode 100644 inst/shinylive/apps/ae-forest-plot/app.R create mode 100644 inst/shinylive/apps/custom-field/app.R create mode 100644 inst/shinylive/apps/dataset-block/app.R create mode 100644 inst/shinylive/apps/empty-stack/app.R create mode 100644 inst/shinylive/apps/filesbrowser-block/app.R create mode 100644 inst/shinylive/apps/filesbrowser-block/penguins.csv create mode 100644 inst/shinylive/apps/ggplot-block/app.R create mode 100644 inst/shinylive/apps/ode-demo/app.R create mode 100644 inst/shinylive/apps/registry-demo/app.R create mode 100644 inst/shinylive/apps/result-block/app.R create mode 100644 inst/shinylive/apps/upload-block/app.R create mode 100644 inst/shinylive/apps/workspace-kitchen-sink/app.R create mode 100644 man/figures/README-unnamed-chunk-3-1.png rename vignettes/{blockr_examples.Rmd => blockr-examples.Rmd} (59%) rename vignettes/{developer-guide.Rmd => internals.Rmd} (98%) diff --git a/DESCRIPTION b/DESCRIPTION index d9643d87..d6ea5083 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ Authors@R: ) Description: A framework for data manipulation and visualization using a web-based point and click user interface. -URL: https://blockr-org.github.io/blockr, https://blockr-org.github.io/blockr/ +URL: https://bristolmyerssquibb.github.io/blockr License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) @@ -41,7 +41,7 @@ Imports: rlang, shinyWidgets Remotes: - DivadNojnarg/DiagrammeR + DivadNojnarg/DiagrammeR, BristolMyersSquibb/blockr.data Suggests: knitr, diff --git a/R/sysdata.rda b/R/sysdata.rda index bdcc9d6575ea5dfd371e30948e6bdc9ccc7ff39f..09140cb7bbc1c2c06180a533a5003b81bdebedb3 100644 GIT binary patch literal 12555 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 literal 1056 zcmV+*1mF8YT4*^jL0KkKSz0ku0ssLge}MilzyJsf|MEZo|M0*6|L_0-Km#2+I6CIt zQ@F~0lW3=j57DBYk?3it^)&GrPbQjrXcXE{QMDP745|K_CYY)CXb{m)z|bdOD^qW}pB7A`>PFPZaV!CR5UBSi7=Q)?5ePv5wA(kZ;qkkDT_)-xx66P% z!OOlne|(>lq=+amSUD)C9Cb5#4VLTYu3=Yd2Cr`XErY;9b(=GFQ9)gs0CU4cb{@*T z$bsRQnlQK!WBxS}%*_duaXhcbjD}XpB=a|E<73ho-HsE4yt-V0sTnW5JkPZ_VFsXF zGvLQ(RvuK!x!uhC&be=&S6k02@*FF!o=TO*hW08cNZB+lIDTr&MV6eW9otvnI#Wqy zgx!X+cJ%9OG*=t$bBp_0lRgFyQSJWN9}e$-qi3Fb-LDuX^dHu9QnVy=$@mST?n|lG5#Z7H`q=ROB=?5+r=ZqaxbRT~eHH7e`i*&yqGfLE&p} zvYQ#D*s9K+`{Bii@?fe#lPq5kyYNOjIfZ;LZ@=cQHjM%3z1;0kBy{}C=_q}3y>5Jth~50$IuX$W?$r6=K(P{82zFk> zaTc{Grr!GMyfn(_mhTu_eQb8#*`BCPPq6$(uTlW-jmzO#MS;^}ho~ne#eR{E diff --git a/R/utils.R b/R/utils.R index b156b116..7dce5307 100644 --- a/R/utils.R +++ b/R/utils.R @@ -331,16 +331,16 @@ has_method <- function(x, generic) { create_app_link <- function(url, mode = c("app", "editor"), header = TRUE) { mode <- match.arg(mode) - if (mode != "editor") app_url <- gsub("editor", mode, url) + if (mode != "editor") url <- gsub("editor", mode, url) if (!header) { - app_url <- paste0(app_url, "&h=0") + url <- paste0(url, "&h=0") } tags$iframe( # To allow the content to fill the full screen card class = "html-fill-item", - src = app_url, + src = url, height = "700", width = "100%", style = "border: 1px solid rgba(0,0,0,0.175); border-radius: .375rem;", @@ -357,8 +357,10 @@ code_chunk <- function(output, language = "r") { } print_shinylive_r_code <- function(name) { - path <- sprintf("inst/shinylive/apps/%s/app.R", name) - code_chunk(cat(paste(readLines(path)[-1], collapse = "\n"))) + path <- system.file(sprintf("shinylive/apps/%s/app.R", name), package = "blockr") + lines <- readLines(path) + to_remove <- grep("webr::", lines) + code_chunk(cat(paste(lines[-to_remove], collapse = "\n"))) } get_block_title <- function(x) { diff --git a/README.Rmd b/README.Rmd index a7cacafa..7d4104c2 100644 --- a/README.Rmd +++ b/README.Rmd @@ -28,48 +28,35 @@ library(DiagrammeR) ## Why blockr? -`{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and __code-free__ approach to building data pipelines. It allows users to create __powerful__ data workflows using pre-built __blocks__ that can be easily __connected__, all without writing a single line of code. - -```{r, echo=FALSE, eval=TRUE, message=FALSE} -mermaid(" - flowchart TD - subgraph LR workspace[Workspace] - subgraph stack1[Stack] - direction LR - subgraph input_block[Block 1] - input(Data: dataset, browser, ...) - end - subgraph transform_block[Block 2] - transform(Transform block: filter, select ...) - end - subgraph output_block[Block 3] - output(Result/transform: plot, filter, ...) - end - input_block --> |data| transform_block --> |data| output_block - end - subgraph stack2[Stack 2] - stack1_data[Stack 1 data] --> |data| transform2[Transform] - end - stack1 --> |data| stack2 - subgraph stackn[Stack n] - stacki_data[Stack i data] --> |data| transformn[Transform] --> |data| Visualize - end - stack2 ---> |... data| stackn - end - ", - width = "100%" -) |> - htmlwidgets::onRender( - "function(el, x) { - el.classList.add('text-center') - } - " - ) +`{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and __code-free__ approach to building data pipelines. + +## Who is it for? + +`{blockr}` has 2 main user targets: + +1. On the one hand, it empowers __non technical__ users to create insightful data workflows using pre-built blocks that can be __easily__ connected, all without writing a single line of code. + +Below is a simple pre-built case study involving `{blockr}`. We use the palmerpenguins dataset to find out which femal species has the largest flippers. This tiny dashboard is composed of 4 steps: import the data, filter, create the plot and chose the geometry (points). Within each step (block), the user can change inputs and see the changes propagate in real time. Notice that the filter step requires to press a submit button before moving forward, which prevents the plot from appearing first. This is to prevent long running task from being run unecessarily. You can find more in other vignettes. + +```{r blockr-penguins-stack, echo=FALSE, fig.cap='Penguins app demo', fig.align = 'center', out.width='100%'} +knitr::include_graphics("vignettes/figures/blockr-penguins-stack.png") +``` + +You can of course start with a totally empty dashboard and create your own analysis from scratch. + +2. On the other hand, it provides __developers__ with a set of tools to seamlessly create new blocks, thereby enhancing the entire framework and fostering __collaboration__ within organizations teams. For instance, regarding the previous example, below is what it takes to create such dashboard. + +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +blockr:::print_shinylive_r_code("palmer-penguins") ``` -To get started, we invite you to read this [vignette](https://blockr-org.github.io/blockr/articles/blockr.html). +Note that the `{blockr.ggplot2}` [package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes some ready to use blocks as shown above. + +## How to get started? -To get a better idea of `{blockr}` capabilities in various data context, you can look at this [vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). +To get started, we invite you to read this [vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr.html). + +To get a better idea of `{blockr}` capabilities in various data context, you can look at this [vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr_examples.html). ## Key features @@ -87,63 +74,6 @@ You can install the development version of blockr from [GitHub](https://github.c pak::pak("BristolMyersSquibb/blockr") ``` -## Example: palmer penguins case study - -Below is a simple case study involving `{blockr}`. We use the palmerpenguins dataset to find out which -femal species has the largest flippers. We create 2 custom blocks allowing to create our plot block (see the plot vignette for more details). Note that the `{blockr.ggplot2}` package exposes some ready to use blocks. - -```{r, eval=FALSE} -library(blockr) -library(palmerpenguins) -library(ggplot2) - -new_ggplot_block <- function(col_x = character(), col_y = character(), ...) { - - data_cols <- function(data) colnames(data) - - new_block( - fields = list( - x = new_select_field(col_x, data_cols, type = "name"), - y = new_select_field(col_y, data_cols, type = "name") - ), - expr = quote( - ggplot(mapping = aes(x = .(x), y = .(y))) - ), - class = c("ggplot_block", "plot_block"), - ... - ) -} - -new_geompoint_block <- function(color = character(), shape = character(), ...) { - - data_cols <- function(data) colnames(data$data) - - new_block( - fields = list( - color = new_select_field(color, data_cols, type = "name"), - shape = new_select_field(shape, data_cols, type = "name") - ), - expr = quote( - geom_point(aes(color = .(color), shape = .(shape)), size = 2) - ), - class = c("plot_layer_block", "plot_block"), - ... - ) -} - -stack <- new_stack( - data_block = new_dataset_block("penguins", "palmerpenguins"), - filter_block = new_filter_block("sex", "female"), - plot_block = new_ggplot_block("flipper_length_mm", "body_mass_g"), - layer_block = new_geompoint_block("species", "species") -) -serve_stack(stack) -``` - -```{r blockr-penguins-stack, echo=FALSE, fig.cap='Penguins app demo', fig.align = 'center', out.width='100%'} -knitr::include_graphics("vignettes/figures/blockr-penguins-stack.png") -``` - ## Contribute Easiest is to run `make`, otherwise: diff --git a/README.md b/README.md index 71c6ba7c..698dc2b0 100644 --- a/README.md +++ b/README.md @@ -15,48 +15,43 @@ `{blockr}` is an R package designed to democratize data analysis by providing a flexible, intuitive, and **code-free** approach to building -data pipelines. It allows users to create **powerful** data workflows -using pre-built **blocks** that can be easily **connected**, all without -writing a single line of code. +data pipelines. -![](man/figures/README-unnamed-chunk-2-1.png) +## Who is it for? -To get started, we invite you to read this -[vignette](https://blockr-org.github.io/blockr/articles/blockr.html). - -To get a better idea of `{blockr}` capabilities in various data context, -you can look at this -[vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). +`{blockr}` has 2 main user targets: -## Key features +1. On the one hand, it empowers **non technical** users to create + insightful data workflows using pre-built blocks that can be + **easily** connected, all without writing a single line of code. -1. **User-Friendly Interface**: Build data pipelines with intuitive - interface. -2. **Flexibility**: Easily add, remove, or rearrange blocks in your - pipeline. -3. **Extensibility**: Developers can create custom blocks to extend - functionality. -4. **Reproducibility**: Pipelines created with `blockr` are easily - shareable and reproducible, with exportable code. -5. **Interactivity**: Real-time feedback as you build and modify your - pipeline. +Below is a simple pre-built case study involving `{blockr}`. We use the +palmerpenguins dataset to find out which femal species has the largest +flippers. This tiny dashboard is composed of 4 steps: import the data, +filter, create the plot and chose the geometry (points). Within each +step (block), the user can change inputs and see the changes propagate +in real time. Notice that the filter step requires to press a submit +button before moving forward, which prevents the plot from appearing +first. This is to prevent long running task from being run unecessarily. +You can find more in other vignettes. -## Installation +
-You can install the development version of blockr from -[GitHub](https://github.com/) with: +Penguins app demo +

+Penguins app demo +

-``` r -pak::pak("blockr-org/blockr") -``` +
-## Example: palmer penguins case study +You can of course start with a totally empty dashboard and create your +own analysis from scratch. -Below is a simple case study involving `{blockr}`. We use the -palmerpenguins dataset to find out which femal species has the largest -flippers. We create 2 custom blocks allowing to create our plot block -(see the plot vignette for more details). Note that the -`{blockr.ggplot2}` package exposes some ready to use blocks. +2. On the other hand, it provides **developers** with a set of tools to + seamlessly create new blocks, thereby enhancing the entire framework + and fostering **collaboration** within organizations teams. For + instance, regarding the previous example, below is what it takes to + create such dashboard. ``` r library(blockr) @@ -106,14 +101,40 @@ stack <- new_stack( serve_stack(stack) ``` -
+Note that the `{blockr.ggplot2}` +[package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes +some ready to use blocks as shown above. -Penguins app demo -

-Penguins app demo -

+## How to get started? -
+To get started, we invite you to read this +[vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr.html). + +To get a better idea of `{blockr}` capabilities in various data context, +you can look at this +[vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr_examples.html). + +## Key features + +1. **User-Friendly Interface**: Build data pipelines with intuitive + interface. +2. **Flexibility**: Easily add, remove, or rearrange blocks in your + pipeline. +3. **Extensibility**: Developers can create custom blocks to extend + functionality. +4. **Reproducibility**: Pipelines created with `blockr` are easily + shareable and reproducible, with exportable code. +5. **Interactivity**: Real-time feedback as you build and modify your + pipeline. + +## Installation + +You can install the development version of blockr from +[GitHub](https://github.com/) with: + +``` r +pak::pak("BristolMyersSquibb/blockr") +``` ## Contribute diff --git a/_pkgdown.yml b/_pkgdown.yml index ca9d55fd..5e7cd016 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,4 +1,4 @@ -url: https://blockr-org.github.io/blockr/ +url: https://bristolmyerssquibb.github.io/blockr/ home: title: Building blocks for data manipulation and visualization operations. @@ -14,16 +14,23 @@ template: -articles: -- title: User guide - navbar: ~ - contents: - - blockr_examples - - data-blocks - - registry - - plot-block - - new-field - - developer-guide +navbar: + components: + articles: + text: Developer guides + menu: + - text: Data blocks + href: articles/data-blocks.html + - text: Block registry + href: articles/registry.html + - text: How to create a plot block? + href: articles/plot-block.html + - text: How to create a new field? + href: articles/new-field.html + - text: More examples + href: articles/blockr-examples.html + - text: Internals + href: articles/internals.html reference: - title: Fields core elements @@ -201,4 +208,3 @@ news: releases: - text: "blockr 0.0.2" - text: "blockr 0.0.1.9000" - \ No newline at end of file diff --git a/index.Rmd b/index.Rmd index 5ee9bb28..8417c1f6 100644 --- a/index.Rmd +++ b/index.Rmd @@ -57,53 +57,16 @@ You can of course start with a totally empty dashboard and create your own analy 2. On the other hand, it provides __developers__ with a set of tools to seamlessly create new blocks, thereby enhancing the entire framework and fostering __collaboration__ within organizations teams. For instance, regarding the previous example, below is what it takes to create such dashboard. ```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} -print_shinylive_r_code("palmer-penguins") +blockr:::print_shinylive_r_code("palmer-penguins") ``` Note that the `{blockr.ggplot2}` [package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes some ready to use blocks as shown above. -## blockr at a glance - -```{r, echo=FALSE, message=FALSE} -mermaid(" - flowchart TD - subgraph LR workspace[Workspace] - subgraph stack1[Stack] - direction LR - subgraph input_block[Block 1] - input(Data: dataset, browser, ...) - end - subgraph transform_block[Block 2] - transform(Transform block: filter, select ...) - end - subgraph output_block[Block 3] - output(Result/transform: plot, filter, ...) - end - input_block --> |data| transform_block --> |data| output_block - end - subgraph stack2[Stack 2] - stack1_data[Stack 1 data] --> |data| transform2[Transform] - end - stack1 --> |data| stack2 - subgraph stackn[Stack n] - stacki_data[Stack i data] --> |data| transformn[Transform] --> |data| Visualize - end - stack2 ---> |... data| stackn - end - ", - width = "100%" -) |> - htmlwidgets::onRender( - "function(el, x) { - el.classList.add('text-center') - } - " - ) -``` +## How to get started? -To get started, we invite you to read this [vignette](https://blockr-org.github.io/blockr/articles/blockr.html). +To get started, we invite you to read this [vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr.html). -To get a better idea of `{blockr}` capabilities in various data context, you can look at this [vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). +To get a better idea of `{blockr}` capabilities in various data context, you can look at this [vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr_examples.html). ## Key features diff --git a/index.md b/index.md index f0ad812f..92a44bf0 100644 --- a/index.md +++ b/index.md @@ -35,9 +35,9 @@ button before moving forward, which prevents the plot from appearing first. This is to prevent long running task from being run unecessarily. You can find more in other vignettes. -
+
- +
@@ -107,16 +107,14 @@ Note that the `{blockr.ggplot2}` [package](https://github.com/BristolMyersSquibb/blockr.ggplot2) exposes some ready to use blocks as shown above. -## blockr at a glance - -![](man/figures/README-unnamed-chunk-4-1.png) +## How to get started? To get started, we invite you to read this -[vignette](https://blockr-org.github.io/blockr/articles/blockr.html). +[vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr.html). To get a better idea of `{blockr}` capabilities in various data context, you can look at this -[vignette](https://blockr-org.github.io/blockr/articles/blockr_examples.html). +[vignette](https://bristolmyerssquibb.github.io/blockr/articles/blockr_examples.html). ## Key features diff --git a/inst/shinylive/apps/ae-forest-plot/app.R b/inst/shinylive/apps/ae-forest-plot/app.R new file mode 100644 index 00000000..c536ebfd --- /dev/null +++ b/inst/shinylive/apps/ae-forest-plot/app.R @@ -0,0 +1,159 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) #nolint +webr::install("blockr.pharmaverseadam", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) #nolint + +library(blockr) +library(dplyr) +library(tidyr) +library(forestplot) +library(blockr.pharmaverseadam) + +# Function to create adverse event forest plot +create_ae_forest_plot <- function(data, usubjid_col, arm_col, aedecod_col, n_events) { + data <- data |> filter(.data[[arm_col]] != "Placebo") + # Convert column names to strings + usubjid_col <- as.character(substitute(usubjid_col)) + arm_col <- as.character(substitute(arm_col)) + aedecod_col <- as.character(substitute(aedecod_col)) + + # Calculate the total number of subjects in each arm + n_subjects <- data |> + select(all_of(c(usubjid_col, arm_col))) |> + distinct() |> + group_by(across(all_of(arm_col))) |> + summarise(n = n(), .groups = "drop") + + # Calculate AE frequencies and proportions + ae_summary <- data |> + group_by(across(all_of(c(arm_col, aedecod_col)))) |> + summarise(n_events = n_distinct(.data[[usubjid_col]]), .groups = "drop") |> + left_join(n_subjects, by = arm_col) |> + mutate(proportion = n_events / n) + + # Select top N most frequent AEs across all arms + top_aes <- ae_summary |> + group_by(across(all_of(aedecod_col))) |> + summarise(total_events = sum(n_events), .groups = "drop") |> + top_n(n_events, total_events) |> + pull(all_of(aedecod_col)) + + # Get unique treatment arms + arms <- unique(data[[arm_col]]) + if (length(arms) != 2) { + stop("This plot requires exactly two treatment arms.") + } + active_arm <- arms[1] + control_arm <- arms[2] + + # Filter for top AEs and calculate relative risk + ae_rr <- ae_summary |> + filter(.data[[aedecod_col]] %in% top_aes) |> + pivot_wider( + id_cols = all_of(aedecod_col), + names_from = all_of(arm_col), + values_from = c(n_events, n, proportion) + ) |> + mutate( + RR = .data[[paste0("proportion_", active_arm)]] / .data[[paste0("proportion_", control_arm)]], + lower_ci = exp(log(RR) - 1.96 * sqrt( + 1 / .data[[paste0("n_events_", active_arm)]] + + 1 / .data[[paste0("n_events_", control_arm)]] - + 1 / .data[[paste0("n_", active_arm)]] - + 1 / .data[[paste0("n_", control_arm)]] + )), + upper_ci = exp(log(RR) + 1.96 * sqrt( + 1 / .data[[paste0("n_events_", active_arm)]] + + 1 / .data[[paste0("n_events_", control_arm)]] - + 1 / .data[[paste0("n_", active_arm)]] - + 1 / .data[[paste0("n_", control_arm)]] + )) + ) + + # Prepare data for forest plot + forest_data <- ae_rr |> + mutate( + label = paste0( + .data[[aedecod_col]], " (", + .data[[paste0("n_events_", active_arm)]], "/", .data[[paste0("n_", active_arm)]], " vs ", + .data[[paste0("n_events_", control_arm)]], "/", .data[[paste0("n_", control_arm)]], ")" + ) + ) + + # Create forest plot + forestplot( + labeltext = cbind( + forest_data$label, + sprintf("%.2f (%.2f-%.2f)", forest_data$RR, forest_data$lower_ci, forest_data$upper_ci) + ), + mean = forest_data$RR, + lower = forest_data$lower_ci, + upper = forest_data$upper_ci, + align = c("l", "r"), + graphwidth = unit(60, "mm"), + cex = 0.9, + lineheight = unit(8, "mm"), + boxsize = 0.35, + col = fpColors(box = "royalblue", line = "darkblue", summary = "royalblue"), + txt_gp = fpTxtGp(label = gpar(cex = 0.9), ticks = gpar(cex = 0.9), xlab = gpar(cex = 0.9)), + xlab = paste("Relative Risk (", active_arm, " / ", control_arm, ")"), + zero = 1, + lwd.zero = 2, + lwd.ci = 2, + xticks = c(0.5, 1, 2, 4), + grid = TRUE, + title = paste("Relative Risk of Adverse Events (", active_arm, " vs ", control_arm, ")") + ) +} + +new_forest_plot_block <- function(...) { + new_block( + fields = list( + usubjid_col = new_select_field( + "USUBJID", + function(data) colnames(data), + multiple = FALSE, + title = "Subject ID Column" + ), + arm_col = new_select_field( + "ACTARM", + function(data) colnames(data), + multiple = FALSE, + title = "Treatment Arm Column" + ), + aedecod_col = new_select_field( + "AEDECOD", + function(data) colnames(data), + multiple = FALSE, + title = "AE Term Column" + ), + n_events = new_numeric_field( + 10, + min = 5, max = 20, step = 1, + title = "Number of Top AEs to Display" + ) + ), + expr = substitute({ + my_fun(data, .(usubjid_col), .(arm_col), .(aedecod_col), .(n_events)) + }, list(my_fun = create_ae_forest_plot)), + class = c("adverse_event_plot_block", "plot_block"), + ... + ) +} + +# Register the custom block +register_block( + new_forest_plot_block, + name = "Adverse Event Forest Plot", + description = "Create a forest plot of adverse events comparing two treatment arms", + classes = c("adverse_event_plot_block", "plot_block"), + input = "data.frame", + output = "plot" +) + +# Create the stack +clinical_trial_stack <- new_stack( + new_adam_block(selected = "adae"), + # filter_in_block(), + new_forest_plot_block() +) + +serve_stack(clinical_trial_stack) diff --git a/inst/shinylive/apps/custom-field/app.R b/inst/shinylive/apps/custom-field/app.R new file mode 100644 index 00000000..808d7ea2 --- /dev/null +++ b/inst/shinylive/apps/custom-field/app.R @@ -0,0 +1,98 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) + +new_slider_field <- function( + value = numeric(), + min = numeric(), + max = numeric(), + step = numeric(), + ...) { + blockr::new_field( + value = value, + min = min, + max = max, + step = step, + ..., + class = "slider_field" + ) +} + +ui_input.slider_field <- function(x, id, name) { + shiny::sliderInput( + blockr::input_ids(x, id), + name, + value = blockr::value(x, "value"), + min = blockr::value(x, "min"), + max = blockr::value(x, "max"), + step = blockr::value(x, "step") + ) +} + +validate_field.slider_field <- function(x) { + val <- value(x) + min <- value(x, "min") + max <- value(x, "max") + step <- value(x, "step") + + validate_number(val) + + if (length(min)) { + validate_number(min, "min") + } + + if (length(max)) { + validate_number(max, "max") + } + + if (length(step)) { + validate_number(step, "step") + } + + NextMethod() +} + +ui_update.slider_field <- function(x, session, id, name) { + updateSliderInput( + session, + blockr::input_ids(x, id), + name, + blockr::value(x), + blockr::value(x, "min"), + blockr::value(x, "max"), + blockr::value(x, "step") + ) +} + +registerS3method("ui_input", "slider_field", ui_input.slider_field) +registerS3method("ui_update", "slider_field", ui_update.slider_field) + +new_slice_block <- function(from = 0, ...) { + + n_rows <- \(data) nrow(data) + + fields <- list( + rows = new_slider_field( + value = from, + min = 0, + max = n_rows, + step = 1, + title = "Select rows" + ) + ) + + new_block( + fields = fields, + expr = quote(dplyr::slice(seq_len(.(rows)))), + name = "Slider slice block", + ..., + class = c("slice_block", "transform_block") + ) +} + +serve_stack( + new_stack( + new_dataset_block("iris"), + new_slice_block(5) + ) +) diff --git a/inst/shinylive/apps/dataset-block/app.R b/inst/shinylive/apps/dataset-block/app.R new file mode 100644 index 00000000..e0714c91 --- /dev/null +++ b/inst/shinylive/apps/dataset-block/app.R @@ -0,0 +1,16 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) +custom_data_block <- function(...) { + new_dataset_block( + selected = "lab", + package = "blockr.data", + ... + ) +} + +stack <- new_stack( + custom_data_block, + new_select_block +) +serve_stack(stack) diff --git a/inst/shinylive/apps/empty-stack/app.R b/inst/shinylive/apps/empty-stack/app.R new file mode 100644 index 00000000..00d69061 --- /dev/null +++ b/inst/shinylive/apps/empty-stack/app.R @@ -0,0 +1,11 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos/", "https://repo.r-wasm.org")) #nolint + +library(blockr) +library(blockr.data) +library(blockr.ggplot2) +library(blockr.cardinal) +library(blockr.pharmaverseadam) +library(blockr.pharmaversesdtm) +library(blockr.ggstatsplot) +library(blockr.clinical.timelines) +serve_stack(new_stack(), id = "mystack") diff --git a/inst/shinylive/apps/filesbrowser-block/app.R b/inst/shinylive/apps/filesbrowser-block/app.R new file mode 100644 index 00000000..4e8689b7 --- /dev/null +++ b/inst/shinylive/apps/filesbrowser-block/app.R @@ -0,0 +1,13 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +## file: app.R +library(blockr) + +stack <- new_stack( + new_filesbrowser_block, + new_csv_block, + new_select_block +) +serve_stack(stack) + +## file: penguins.csv diff --git a/inst/shinylive/apps/filesbrowser-block/penguins.csv b/inst/shinylive/apps/filesbrowser-block/penguins.csv new file mode 100644 index 00000000..03e2c584 --- /dev/null +++ b/inst/shinylive/apps/filesbrowser-block/penguins.csv @@ -0,0 +1,345 @@ +"species","island","bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g","sex","year" +"Adelie","Torgersen",39.1,18.7,181,3750,"male",2007 +"Adelie","Torgersen",39.5,17.4,186,3800,"female",2007 +"Adelie","Torgersen",40.3,18,195,3250,"female",2007 +"Adelie","Torgersen",NA,NA,NA,NA,NA,2007 +"Adelie","Torgersen",36.7,19.3,193,3450,"female",2007 +"Adelie","Torgersen",39.3,20.6,190,3650,"male",2007 +"Adelie","Torgersen",38.9,17.8,181,3625,"female",2007 +"Adelie","Torgersen",39.2,19.6,195,4675,"male",2007 +"Adelie","Torgersen",34.1,18.1,193,3475,NA,2007 +"Adelie","Torgersen",42,20.2,190,4250,NA,2007 +"Adelie","Torgersen",37.8,17.1,186,3300,NA,2007 +"Adelie","Torgersen",37.8,17.3,180,3700,NA,2007 +"Adelie","Torgersen",41.1,17.6,182,3200,"female",2007 +"Adelie","Torgersen",38.6,21.2,191,3800,"male",2007 +"Adelie","Torgersen",34.6,21.1,198,4400,"male",2007 +"Adelie","Torgersen",36.6,17.8,185,3700,"female",2007 +"Adelie","Torgersen",38.7,19,195,3450,"female",2007 +"Adelie","Torgersen",42.5,20.7,197,4500,"male",2007 +"Adelie","Torgersen",34.4,18.4,184,3325,"female",2007 +"Adelie","Torgersen",46,21.5,194,4200,"male",2007 +"Adelie","Biscoe",37.8,18.3,174,3400,"female",2007 +"Adelie","Biscoe",37.7,18.7,180,3600,"male",2007 +"Adelie","Biscoe",35.9,19.2,189,3800,"female",2007 +"Adelie","Biscoe",38.2,18.1,185,3950,"male",2007 +"Adelie","Biscoe",38.8,17.2,180,3800,"male",2007 +"Adelie","Biscoe",35.3,18.9,187,3800,"female",2007 +"Adelie","Biscoe",40.6,18.6,183,3550,"male",2007 +"Adelie","Biscoe",40.5,17.9,187,3200,"female",2007 +"Adelie","Biscoe",37.9,18.6,172,3150,"female",2007 +"Adelie","Biscoe",40.5,18.9,180,3950,"male",2007 +"Adelie","Dream",39.5,16.7,178,3250,"female",2007 +"Adelie","Dream",37.2,18.1,178,3900,"male",2007 +"Adelie","Dream",39.5,17.8,188,3300,"female",2007 +"Adelie","Dream",40.9,18.9,184,3900,"male",2007 +"Adelie","Dream",36.4,17,195,3325,"female",2007 +"Adelie","Dream",39.2,21.1,196,4150,"male",2007 +"Adelie","Dream",38.8,20,190,3950,"male",2007 +"Adelie","Dream",42.2,18.5,180,3550,"female",2007 +"Adelie","Dream",37.6,19.3,181,3300,"female",2007 +"Adelie","Dream",39.8,19.1,184,4650,"male",2007 +"Adelie","Dream",36.5,18,182,3150,"female",2007 +"Adelie","Dream",40.8,18.4,195,3900,"male",2007 +"Adelie","Dream",36,18.5,186,3100,"female",2007 +"Adelie","Dream",44.1,19.7,196,4400,"male",2007 +"Adelie","Dream",37,16.9,185,3000,"female",2007 +"Adelie","Dream",39.6,18.8,190,4600,"male",2007 +"Adelie","Dream",41.1,19,182,3425,"male",2007 +"Adelie","Dream",37.5,18.9,179,2975,NA,2007 +"Adelie","Dream",36,17.9,190,3450,"female",2007 +"Adelie","Dream",42.3,21.2,191,4150,"male",2007 +"Adelie","Biscoe",39.6,17.7,186,3500,"female",2008 +"Adelie","Biscoe",40.1,18.9,188,4300,"male",2008 +"Adelie","Biscoe",35,17.9,190,3450,"female",2008 +"Adelie","Biscoe",42,19.5,200,4050,"male",2008 +"Adelie","Biscoe",34.5,18.1,187,2900,"female",2008 +"Adelie","Biscoe",41.4,18.6,191,3700,"male",2008 +"Adelie","Biscoe",39,17.5,186,3550,"female",2008 +"Adelie","Biscoe",40.6,18.8,193,3800,"male",2008 +"Adelie","Biscoe",36.5,16.6,181,2850,"female",2008 +"Adelie","Biscoe",37.6,19.1,194,3750,"male",2008 +"Adelie","Biscoe",35.7,16.9,185,3150,"female",2008 +"Adelie","Biscoe",41.3,21.1,195,4400,"male",2008 +"Adelie","Biscoe",37.6,17,185,3600,"female",2008 +"Adelie","Biscoe",41.1,18.2,192,4050,"male",2008 +"Adelie","Biscoe",36.4,17.1,184,2850,"female",2008 +"Adelie","Biscoe",41.6,18,192,3950,"male",2008 +"Adelie","Biscoe",35.5,16.2,195,3350,"female",2008 +"Adelie","Biscoe",41.1,19.1,188,4100,"male",2008 +"Adelie","Torgersen",35.9,16.6,190,3050,"female",2008 +"Adelie","Torgersen",41.8,19.4,198,4450,"male",2008 +"Adelie","Torgersen",33.5,19,190,3600,"female",2008 +"Adelie","Torgersen",39.7,18.4,190,3900,"male",2008 +"Adelie","Torgersen",39.6,17.2,196,3550,"female",2008 +"Adelie","Torgersen",45.8,18.9,197,4150,"male",2008 +"Adelie","Torgersen",35.5,17.5,190,3700,"female",2008 +"Adelie","Torgersen",42.8,18.5,195,4250,"male",2008 +"Adelie","Torgersen",40.9,16.8,191,3700,"female",2008 +"Adelie","Torgersen",37.2,19.4,184,3900,"male",2008 +"Adelie","Torgersen",36.2,16.1,187,3550,"female",2008 +"Adelie","Torgersen",42.1,19.1,195,4000,"male",2008 +"Adelie","Torgersen",34.6,17.2,189,3200,"female",2008 +"Adelie","Torgersen",42.9,17.6,196,4700,"male",2008 +"Adelie","Torgersen",36.7,18.8,187,3800,"female",2008 +"Adelie","Torgersen",35.1,19.4,193,4200,"male",2008 +"Adelie","Dream",37.3,17.8,191,3350,"female",2008 +"Adelie","Dream",41.3,20.3,194,3550,"male",2008 +"Adelie","Dream",36.3,19.5,190,3800,"male",2008 +"Adelie","Dream",36.9,18.6,189,3500,"female",2008 +"Adelie","Dream",38.3,19.2,189,3950,"male",2008 +"Adelie","Dream",38.9,18.8,190,3600,"female",2008 +"Adelie","Dream",35.7,18,202,3550,"female",2008 +"Adelie","Dream",41.1,18.1,205,4300,"male",2008 +"Adelie","Dream",34,17.1,185,3400,"female",2008 +"Adelie","Dream",39.6,18.1,186,4450,"male",2008 +"Adelie","Dream",36.2,17.3,187,3300,"female",2008 +"Adelie","Dream",40.8,18.9,208,4300,"male",2008 +"Adelie","Dream",38.1,18.6,190,3700,"female",2008 +"Adelie","Dream",40.3,18.5,196,4350,"male",2008 +"Adelie","Dream",33.1,16.1,178,2900,"female",2008 +"Adelie","Dream",43.2,18.5,192,4100,"male",2008 +"Adelie","Biscoe",35,17.9,192,3725,"female",2009 +"Adelie","Biscoe",41,20,203,4725,"male",2009 +"Adelie","Biscoe",37.7,16,183,3075,"female",2009 +"Adelie","Biscoe",37.8,20,190,4250,"male",2009 +"Adelie","Biscoe",37.9,18.6,193,2925,"female",2009 +"Adelie","Biscoe",39.7,18.9,184,3550,"male",2009 +"Adelie","Biscoe",38.6,17.2,199,3750,"female",2009 +"Adelie","Biscoe",38.2,20,190,3900,"male",2009 +"Adelie","Biscoe",38.1,17,181,3175,"female",2009 +"Adelie","Biscoe",43.2,19,197,4775,"male",2009 +"Adelie","Biscoe",38.1,16.5,198,3825,"female",2009 +"Adelie","Biscoe",45.6,20.3,191,4600,"male",2009 +"Adelie","Biscoe",39.7,17.7,193,3200,"female",2009 +"Adelie","Biscoe",42.2,19.5,197,4275,"male",2009 +"Adelie","Biscoe",39.6,20.7,191,3900,"female",2009 +"Adelie","Biscoe",42.7,18.3,196,4075,"male",2009 +"Adelie","Torgersen",38.6,17,188,2900,"female",2009 +"Adelie","Torgersen",37.3,20.5,199,3775,"male",2009 +"Adelie","Torgersen",35.7,17,189,3350,"female",2009 +"Adelie","Torgersen",41.1,18.6,189,3325,"male",2009 +"Adelie","Torgersen",36.2,17.2,187,3150,"female",2009 +"Adelie","Torgersen",37.7,19.8,198,3500,"male",2009 +"Adelie","Torgersen",40.2,17,176,3450,"female",2009 +"Adelie","Torgersen",41.4,18.5,202,3875,"male",2009 +"Adelie","Torgersen",35.2,15.9,186,3050,"female",2009 +"Adelie","Torgersen",40.6,19,199,4000,"male",2009 +"Adelie","Torgersen",38.8,17.6,191,3275,"female",2009 +"Adelie","Torgersen",41.5,18.3,195,4300,"male",2009 +"Adelie","Torgersen",39,17.1,191,3050,"female",2009 +"Adelie","Torgersen",44.1,18,210,4000,"male",2009 +"Adelie","Torgersen",38.5,17.9,190,3325,"female",2009 +"Adelie","Torgersen",43.1,19.2,197,3500,"male",2009 +"Adelie","Dream",36.8,18.5,193,3500,"female",2009 +"Adelie","Dream",37.5,18.5,199,4475,"male",2009 +"Adelie","Dream",38.1,17.6,187,3425,"female",2009 +"Adelie","Dream",41.1,17.5,190,3900,"male",2009 +"Adelie","Dream",35.6,17.5,191,3175,"female",2009 +"Adelie","Dream",40.2,20.1,200,3975,"male",2009 +"Adelie","Dream",37,16.5,185,3400,"female",2009 +"Adelie","Dream",39.7,17.9,193,4250,"male",2009 +"Adelie","Dream",40.2,17.1,193,3400,"female",2009 +"Adelie","Dream",40.6,17.2,187,3475,"male",2009 +"Adelie","Dream",32.1,15.5,188,3050,"female",2009 +"Adelie","Dream",40.7,17,190,3725,"male",2009 +"Adelie","Dream",37.3,16.8,192,3000,"female",2009 +"Adelie","Dream",39,18.7,185,3650,"male",2009 +"Adelie","Dream",39.2,18.6,190,4250,"male",2009 +"Adelie","Dream",36.6,18.4,184,3475,"female",2009 +"Adelie","Dream",36,17.8,195,3450,"female",2009 +"Adelie","Dream",37.8,18.1,193,3750,"male",2009 +"Adelie","Dream",36,17.1,187,3700,"female",2009 +"Adelie","Dream",41.5,18.5,201,4000,"male",2009 +"Gentoo","Biscoe",46.1,13.2,211,4500,"female",2007 +"Gentoo","Biscoe",50,16.3,230,5700,"male",2007 +"Gentoo","Biscoe",48.7,14.1,210,4450,"female",2007 +"Gentoo","Biscoe",50,15.2,218,5700,"male",2007 +"Gentoo","Biscoe",47.6,14.5,215,5400,"male",2007 +"Gentoo","Biscoe",46.5,13.5,210,4550,"female",2007 +"Gentoo","Biscoe",45.4,14.6,211,4800,"female",2007 +"Gentoo","Biscoe",46.7,15.3,219,5200,"male",2007 +"Gentoo","Biscoe",43.3,13.4,209,4400,"female",2007 +"Gentoo","Biscoe",46.8,15.4,215,5150,"male",2007 +"Gentoo","Biscoe",40.9,13.7,214,4650,"female",2007 +"Gentoo","Biscoe",49,16.1,216,5550,"male",2007 +"Gentoo","Biscoe",45.5,13.7,214,4650,"female",2007 +"Gentoo","Biscoe",48.4,14.6,213,5850,"male",2007 +"Gentoo","Biscoe",45.8,14.6,210,4200,"female",2007 +"Gentoo","Biscoe",49.3,15.7,217,5850,"male",2007 +"Gentoo","Biscoe",42,13.5,210,4150,"female",2007 +"Gentoo","Biscoe",49.2,15.2,221,6300,"male",2007 +"Gentoo","Biscoe",46.2,14.5,209,4800,"female",2007 +"Gentoo","Biscoe",48.7,15.1,222,5350,"male",2007 +"Gentoo","Biscoe",50.2,14.3,218,5700,"male",2007 +"Gentoo","Biscoe",45.1,14.5,215,5000,"female",2007 +"Gentoo","Biscoe",46.5,14.5,213,4400,"female",2007 +"Gentoo","Biscoe",46.3,15.8,215,5050,"male",2007 +"Gentoo","Biscoe",42.9,13.1,215,5000,"female",2007 +"Gentoo","Biscoe",46.1,15.1,215,5100,"male",2007 +"Gentoo","Biscoe",44.5,14.3,216,4100,NA,2007 +"Gentoo","Biscoe",47.8,15,215,5650,"male",2007 +"Gentoo","Biscoe",48.2,14.3,210,4600,"female",2007 +"Gentoo","Biscoe",50,15.3,220,5550,"male",2007 +"Gentoo","Biscoe",47.3,15.3,222,5250,"male",2007 +"Gentoo","Biscoe",42.8,14.2,209,4700,"female",2007 +"Gentoo","Biscoe",45.1,14.5,207,5050,"female",2007 +"Gentoo","Biscoe",59.6,17,230,6050,"male",2007 +"Gentoo","Biscoe",49.1,14.8,220,5150,"female",2008 +"Gentoo","Biscoe",48.4,16.3,220,5400,"male",2008 +"Gentoo","Biscoe",42.6,13.7,213,4950,"female",2008 +"Gentoo","Biscoe",44.4,17.3,219,5250,"male",2008 +"Gentoo","Biscoe",44,13.6,208,4350,"female",2008 +"Gentoo","Biscoe",48.7,15.7,208,5350,"male",2008 +"Gentoo","Biscoe",42.7,13.7,208,3950,"female",2008 +"Gentoo","Biscoe",49.6,16,225,5700,"male",2008 +"Gentoo","Biscoe",45.3,13.7,210,4300,"female",2008 +"Gentoo","Biscoe",49.6,15,216,4750,"male",2008 +"Gentoo","Biscoe",50.5,15.9,222,5550,"male",2008 +"Gentoo","Biscoe",43.6,13.9,217,4900,"female",2008 +"Gentoo","Biscoe",45.5,13.9,210,4200,"female",2008 +"Gentoo","Biscoe",50.5,15.9,225,5400,"male",2008 +"Gentoo","Biscoe",44.9,13.3,213,5100,"female",2008 +"Gentoo","Biscoe",45.2,15.8,215,5300,"male",2008 +"Gentoo","Biscoe",46.6,14.2,210,4850,"female",2008 +"Gentoo","Biscoe",48.5,14.1,220,5300,"male",2008 +"Gentoo","Biscoe",45.1,14.4,210,4400,"female",2008 +"Gentoo","Biscoe",50.1,15,225,5000,"male",2008 +"Gentoo","Biscoe",46.5,14.4,217,4900,"female",2008 +"Gentoo","Biscoe",45,15.4,220,5050,"male",2008 +"Gentoo","Biscoe",43.8,13.9,208,4300,"female",2008 +"Gentoo","Biscoe",45.5,15,220,5000,"male",2008 +"Gentoo","Biscoe",43.2,14.5,208,4450,"female",2008 +"Gentoo","Biscoe",50.4,15.3,224,5550,"male",2008 +"Gentoo","Biscoe",45.3,13.8,208,4200,"female",2008 +"Gentoo","Biscoe",46.2,14.9,221,5300,"male",2008 +"Gentoo","Biscoe",45.7,13.9,214,4400,"female",2008 +"Gentoo","Biscoe",54.3,15.7,231,5650,"male",2008 +"Gentoo","Biscoe",45.8,14.2,219,4700,"female",2008 +"Gentoo","Biscoe",49.8,16.8,230,5700,"male",2008 +"Gentoo","Biscoe",46.2,14.4,214,4650,NA,2008 +"Gentoo","Biscoe",49.5,16.2,229,5800,"male",2008 +"Gentoo","Biscoe",43.5,14.2,220,4700,"female",2008 +"Gentoo","Biscoe",50.7,15,223,5550,"male",2008 +"Gentoo","Biscoe",47.7,15,216,4750,"female",2008 +"Gentoo","Biscoe",46.4,15.6,221,5000,"male",2008 +"Gentoo","Biscoe",48.2,15.6,221,5100,"male",2008 +"Gentoo","Biscoe",46.5,14.8,217,5200,"female",2008 +"Gentoo","Biscoe",46.4,15,216,4700,"female",2008 +"Gentoo","Biscoe",48.6,16,230,5800,"male",2008 +"Gentoo","Biscoe",47.5,14.2,209,4600,"female",2008 +"Gentoo","Biscoe",51.1,16.3,220,6000,"male",2008 +"Gentoo","Biscoe",45.2,13.8,215,4750,"female",2008 +"Gentoo","Biscoe",45.2,16.4,223,5950,"male",2008 +"Gentoo","Biscoe",49.1,14.5,212,4625,"female",2009 +"Gentoo","Biscoe",52.5,15.6,221,5450,"male",2009 +"Gentoo","Biscoe",47.4,14.6,212,4725,"female",2009 +"Gentoo","Biscoe",50,15.9,224,5350,"male",2009 +"Gentoo","Biscoe",44.9,13.8,212,4750,"female",2009 +"Gentoo","Biscoe",50.8,17.3,228,5600,"male",2009 +"Gentoo","Biscoe",43.4,14.4,218,4600,"female",2009 +"Gentoo","Biscoe",51.3,14.2,218,5300,"male",2009 +"Gentoo","Biscoe",47.5,14,212,4875,"female",2009 +"Gentoo","Biscoe",52.1,17,230,5550,"male",2009 +"Gentoo","Biscoe",47.5,15,218,4950,"female",2009 +"Gentoo","Biscoe",52.2,17.1,228,5400,"male",2009 +"Gentoo","Biscoe",45.5,14.5,212,4750,"female",2009 +"Gentoo","Biscoe",49.5,16.1,224,5650,"male",2009 +"Gentoo","Biscoe",44.5,14.7,214,4850,"female",2009 +"Gentoo","Biscoe",50.8,15.7,226,5200,"male",2009 +"Gentoo","Biscoe",49.4,15.8,216,4925,"male",2009 +"Gentoo","Biscoe",46.9,14.6,222,4875,"female",2009 +"Gentoo","Biscoe",48.4,14.4,203,4625,"female",2009 +"Gentoo","Biscoe",51.1,16.5,225,5250,"male",2009 +"Gentoo","Biscoe",48.5,15,219,4850,"female",2009 +"Gentoo","Biscoe",55.9,17,228,5600,"male",2009 +"Gentoo","Biscoe",47.2,15.5,215,4975,"female",2009 +"Gentoo","Biscoe",49.1,15,228,5500,"male",2009 +"Gentoo","Biscoe",47.3,13.8,216,4725,NA,2009 +"Gentoo","Biscoe",46.8,16.1,215,5500,"male",2009 +"Gentoo","Biscoe",41.7,14.7,210,4700,"female",2009 +"Gentoo","Biscoe",53.4,15.8,219,5500,"male",2009 +"Gentoo","Biscoe",43.3,14,208,4575,"female",2009 +"Gentoo","Biscoe",48.1,15.1,209,5500,"male",2009 +"Gentoo","Biscoe",50.5,15.2,216,5000,"female",2009 +"Gentoo","Biscoe",49.8,15.9,229,5950,"male",2009 +"Gentoo","Biscoe",43.5,15.2,213,4650,"female",2009 +"Gentoo","Biscoe",51.5,16.3,230,5500,"male",2009 +"Gentoo","Biscoe",46.2,14.1,217,4375,"female",2009 +"Gentoo","Biscoe",55.1,16,230,5850,"male",2009 +"Gentoo","Biscoe",44.5,15.7,217,4875,NA,2009 +"Gentoo","Biscoe",48.8,16.2,222,6000,"male",2009 +"Gentoo","Biscoe",47.2,13.7,214,4925,"female",2009 +"Gentoo","Biscoe",NA,NA,NA,NA,NA,2009 +"Gentoo","Biscoe",46.8,14.3,215,4850,"female",2009 +"Gentoo","Biscoe",50.4,15.7,222,5750,"male",2009 +"Gentoo","Biscoe",45.2,14.8,212,5200,"female",2009 +"Gentoo","Biscoe",49.9,16.1,213,5400,"male",2009 +"Chinstrap","Dream",46.5,17.9,192,3500,"female",2007 +"Chinstrap","Dream",50,19.5,196,3900,"male",2007 +"Chinstrap","Dream",51.3,19.2,193,3650,"male",2007 +"Chinstrap","Dream",45.4,18.7,188,3525,"female",2007 +"Chinstrap","Dream",52.7,19.8,197,3725,"male",2007 +"Chinstrap","Dream",45.2,17.8,198,3950,"female",2007 +"Chinstrap","Dream",46.1,18.2,178,3250,"female",2007 +"Chinstrap","Dream",51.3,18.2,197,3750,"male",2007 +"Chinstrap","Dream",46,18.9,195,4150,"female",2007 +"Chinstrap","Dream",51.3,19.9,198,3700,"male",2007 +"Chinstrap","Dream",46.6,17.8,193,3800,"female",2007 +"Chinstrap","Dream",51.7,20.3,194,3775,"male",2007 +"Chinstrap","Dream",47,17.3,185,3700,"female",2007 +"Chinstrap","Dream",52,18.1,201,4050,"male",2007 +"Chinstrap","Dream",45.9,17.1,190,3575,"female",2007 +"Chinstrap","Dream",50.5,19.6,201,4050,"male",2007 +"Chinstrap","Dream",50.3,20,197,3300,"male",2007 +"Chinstrap","Dream",58,17.8,181,3700,"female",2007 +"Chinstrap","Dream",46.4,18.6,190,3450,"female",2007 +"Chinstrap","Dream",49.2,18.2,195,4400,"male",2007 +"Chinstrap","Dream",42.4,17.3,181,3600,"female",2007 +"Chinstrap","Dream",48.5,17.5,191,3400,"male",2007 +"Chinstrap","Dream",43.2,16.6,187,2900,"female",2007 +"Chinstrap","Dream",50.6,19.4,193,3800,"male",2007 +"Chinstrap","Dream",46.7,17.9,195,3300,"female",2007 +"Chinstrap","Dream",52,19,197,4150,"male",2007 +"Chinstrap","Dream",50.5,18.4,200,3400,"female",2008 +"Chinstrap","Dream",49.5,19,200,3800,"male",2008 +"Chinstrap","Dream",46.4,17.8,191,3700,"female",2008 +"Chinstrap","Dream",52.8,20,205,4550,"male",2008 +"Chinstrap","Dream",40.9,16.6,187,3200,"female",2008 +"Chinstrap","Dream",54.2,20.8,201,4300,"male",2008 +"Chinstrap","Dream",42.5,16.7,187,3350,"female",2008 +"Chinstrap","Dream",51,18.8,203,4100,"male",2008 +"Chinstrap","Dream",49.7,18.6,195,3600,"male",2008 +"Chinstrap","Dream",47.5,16.8,199,3900,"female",2008 +"Chinstrap","Dream",47.6,18.3,195,3850,"female",2008 +"Chinstrap","Dream",52,20.7,210,4800,"male",2008 +"Chinstrap","Dream",46.9,16.6,192,2700,"female",2008 +"Chinstrap","Dream",53.5,19.9,205,4500,"male",2008 +"Chinstrap","Dream",49,19.5,210,3950,"male",2008 +"Chinstrap","Dream",46.2,17.5,187,3650,"female",2008 +"Chinstrap","Dream",50.9,19.1,196,3550,"male",2008 +"Chinstrap","Dream",45.5,17,196,3500,"female",2008 +"Chinstrap","Dream",50.9,17.9,196,3675,"female",2009 +"Chinstrap","Dream",50.8,18.5,201,4450,"male",2009 +"Chinstrap","Dream",50.1,17.9,190,3400,"female",2009 +"Chinstrap","Dream",49,19.6,212,4300,"male",2009 +"Chinstrap","Dream",51.5,18.7,187,3250,"male",2009 +"Chinstrap","Dream",49.8,17.3,198,3675,"female",2009 +"Chinstrap","Dream",48.1,16.4,199,3325,"female",2009 +"Chinstrap","Dream",51.4,19,201,3950,"male",2009 +"Chinstrap","Dream",45.7,17.3,193,3600,"female",2009 +"Chinstrap","Dream",50.7,19.7,203,4050,"male",2009 +"Chinstrap","Dream",42.5,17.3,187,3350,"female",2009 +"Chinstrap","Dream",52.2,18.8,197,3450,"male",2009 +"Chinstrap","Dream",45.2,16.6,191,3250,"female",2009 +"Chinstrap","Dream",49.3,19.9,203,4050,"male",2009 +"Chinstrap","Dream",50.2,18.8,202,3800,"male",2009 +"Chinstrap","Dream",45.6,19.4,194,3525,"female",2009 +"Chinstrap","Dream",51.9,19.5,206,3950,"male",2009 +"Chinstrap","Dream",46.8,16.5,189,3650,"female",2009 +"Chinstrap","Dream",45.7,17,195,3650,"female",2009 +"Chinstrap","Dream",55.8,19.8,207,4000,"male",2009 +"Chinstrap","Dream",43.5,18.1,202,3400,"female",2009 +"Chinstrap","Dream",49.6,18.2,193,3775,"male",2009 +"Chinstrap","Dream",50.8,19,210,4100,"male",2009 +"Chinstrap","Dream",50.2,18.7,198,3775,"female",2009 diff --git a/inst/shinylive/apps/ggplot-block/app.R b/inst/shinylive/apps/ggplot-block/app.R new file mode 100644 index 00000000..ed217089 --- /dev/null +++ b/inst/shinylive/apps/ggplot-block/app.R @@ -0,0 +1,46 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) +library(palmerpenguins) +library(ggplot2) + +new_ggplot_block <- function(col_x = character(), col_y = character(), ...) { + + data_cols <- function(data) colnames(data) + + new_block( + fields = list( + x = new_select_field(col_x, data_cols, type = "name"), + y = new_select_field(col_y, data_cols, type = "name") + ), + expr = quote( + ggplot(mapping = aes(x = .(x), y = .(y))) + ), + class = c("ggplot_block", "plot_block"), + ... + ) +} + +new_geompoint_block <- function(color = character(), shape = character(), ...) { + + data_cols <- function(data) colnames(data$data) + + new_block( + fields = list( + color = new_select_field(color, data_cols, type = "name"), + shape = new_select_field(shape, data_cols, type = "name") + ), + expr = quote( + geom_point(aes(color = .(color), shape = .(shape)), size = 2) + ), + class = c("plot_layer_block", "plot_block"), + ... + ) +} + +stack <- new_stack( + data_block = new_dataset_block("penguins", "palmerpenguins"), + plot_block = new_ggplot_block("flipper_length_mm", "body_mass_g"), + layer_block = new_geompoint_block("species", "species") +) +serve_stack(stack) diff --git a/inst/shinylive/apps/ode-demo/app.R b/inst/shinylive/apps/ode-demo/app.R new file mode 100644 index 00000000..e2fd4c1a --- /dev/null +++ b/inst/shinylive/apps/ode-demo/app.R @@ -0,0 +1,51 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) +webr::install("blockr.ggplot2", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) +library(pracma) +library(blockr.ggplot2) + +new_ode_block <- function(...) { + + lorenz <- function (t, y, parms) { + c( + X = parms[1] * y[1] + y[2] * y[3], + Y = parms[2] * (y[2] - y[3]), + Z = -y[1] * y[2] + parms[3] * y[2] - y[3] + ) + } + + fields <- list( + a = new_numeric_field(-8/3, -10, 20), + b = new_numeric_field(-10, -50, 100), + c = new_numeric_field(28, 1, 100) + ) + + new_block( + fields = fields, + expr = substitute( + as.data.frame( + ode45( + fun, + y0 = c(X = 1, Y = 1, Z = 1), + t0 = 0, + tfinal = 100, + parms = c(.(a), .(b), .(c)) + ) + ), + list(fun = lorenz) + ), + ..., + class = c("ode_block", "data_block") + ) +} + +stack <- new_stack( + new_ode_block, + new_ggplot_block( + func = c("x", "y"), + default_columns = c("y.1", "y.2") + ), + new_geompoint_block +) +serve_stack(stack) diff --git a/inst/shinylive/apps/registry-demo/app.R b/inst/shinylive/apps/registry-demo/app.R new file mode 100644 index 00000000..cecf07d6 --- /dev/null +++ b/inst/shinylive/apps/registry-demo/app.R @@ -0,0 +1,29 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) + +new_tail_block <- function(data, n_rows = numeric(), ...) { + + n_rows_max <- function(data) nrow(data) + + new_block( + fields = list( + n_rows = new_numeric_field(n_rows, 1L, n_rows_max) + ), + expr = quote(tail(n = .(n_rows))), + class = c("tail_block", "transform_block"), + ... + ) +} + +register_block( + constructor = new_tail_block, + name = "tail block", + description = "return last n rows", + classes = c("tail_block", "transform_block"), + input = "data.frame", + output = "data.frame" +) + +stack <- new_stack(new_dataset_block) +serve_stack(stack) diff --git a/inst/shinylive/apps/result-block/app.R b/inst/shinylive/apps/result-block/app.R new file mode 100644 index 00000000..0f751fee --- /dev/null +++ b/inst/shinylive/apps/result-block/app.R @@ -0,0 +1,14 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) +library(blockr.data) + +serve_workspace( + stack1 = new_stack( + new_dataset_block("lab", "blockr.data"), + new_select_block(c("STUDYID", "USUBJID")) + ), + stack2 = new_stack(new_result_block), + stack3 = new_stack(new_dataset_block("ae", "blockr.data")), + title = "My workspace" +) diff --git a/inst/shinylive/apps/upload-block/app.R b/inst/shinylive/apps/upload-block/app.R new file mode 100644 index 00000000..513580ca --- /dev/null +++ b/inst/shinylive/apps/upload-block/app.R @@ -0,0 +1,10 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos", "https://repo.r-wasm.org")) + +library(blockr) + +stack <- new_stack( + new_upload_block, + new_csv_block, + new_select_block +) +serve_stack(stack) diff --git a/inst/shinylive/apps/workspace-kitchen-sink/app.R b/inst/shinylive/apps/workspace-kitchen-sink/app.R new file mode 100644 index 00000000..61f271ff --- /dev/null +++ b/inst/shinylive/apps/workspace-kitchen-sink/app.R @@ -0,0 +1,7 @@ +webr::install("blockr", repos = c("https://bristolmyerssquibb.github.io/webr-repos/", "https://repo.r-wasm.org")) #nolint + +library(blockr) + +do.call(set_workspace, args = list(title = "My workspace")) + +serve_workspace(clear = FALSE) diff --git a/man/create_app_link.Rd b/man/create_app_link.Rd index 7eaec273..f5587016 100644 --- a/man/create_app_link.Rd +++ b/man/create_app_link.Rd @@ -14,5 +14,6 @@ create_app_link(url, mode = c("app", "editor"), header = TRUE) \item{header}{Whether to display the shinylive header. Default to TRUE.} } \description{ -Create shinylive iframe +Useful for pkgdown website } +\keyword{internal} diff --git a/man/figures/README-unnamed-chunk-3-1.png b/man/figures/README-unnamed-chunk-3-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a4809bfee32feaa1cf98af26e311c09169c5e097 GIT binary patch literal 33487 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 Tg0=~ - %\VignetteIndexEntry{Case studies} + %\VignetteIndexEntry{5: Case studies} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -15,7 +15,7 @@ knitr::opts_chunk$set(echo = TRUE) ## blockr Across Industries -The flexibility of `blockr` makes it valuable across various industries. Let's explore how it can be applied in different sectors with detailed examples. Somex examples require to create a new field such as the `new_slider_field` described in the corresponding vignette. +The __flexibility__ of `blockr` makes it valuable across various __industries__. Let's explore how it can be applied in different sectors with detailed examples. Some examples require to create a new field such as the `new_slider_field` described in the corresponding [vignette](https://bristolmyerssquibb.github.io/blockr/articles/new-field.html). ```{r slider_block, include=FALSE} # Provided by John, new_range_field not working @@ -209,9 +209,10 @@ This forest plot visualizes the relative risk of adverse events between two trea ```{r, eval=TRUE, echo=FALSE} card( - create_app_link( - "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-CiCCKmu6w1-qhGBtYuG3uagc2hqLcI9N70WDWATR0MA3BphgCkN1St7E0BaG3g0RwU0JaMWUeg1l8kUiVsTF7Ka6ClGVs2BAOAWg4DVp0D2iADg+AAA5B0wGHaxOgRAvbsBYAAL1JGoCdABmAArCOzU2bUCZlELUfYu6yZNqIDsHgIhnIBGnce3STQGDiA-XAAI+UQrRhfW+7ggGt2yVIGqlk1gq4ltQPbNVwsJHGqmghsoBExHjqUU1R4EgdJYd9KOvDiimpewBZh1BpHcMnvwzGtGVGoB0GBW4zQWBjr4iITILAHdYXoqIZw1lBieXsKyTAAV0G0ZHo4lNZOU7qANAwHJnEahzZKZU65KamnWJqpYERqaGgJ2XpqbITTMgAAsTGIrsSeFNe2WAACqNlK2PFIFhdjtDOPcYxaSfj7BxAASAlZBoxI6gyBsrjQTRLVnlFE5S3SsgmWSek91VMawo7GRndQTkPreSux4iCZs7wRR+T9BgDAVs4JwDywcHVtoR7HQaDpTYnBGsRTqllDadXRrHVApyNy3BtVBKEM5g6zmABCAApAAkgAEWpf0srDExSqy4JaZUxZvgbagLZzZC1vQsAuD+pEVkAAyB03NBI815sDB0QKnUW6ZPMBYhC5MrbaNKvW8uYUG81kbnW0ZCCsume2VksAAFllsIuFGtkge2tuah20Je0B2ClHceKdqa52rs3f6Xdn9Qh7bByevHO9+Z1hCt0Rj77U8z7XtUn1-76IhsteBxFUHNkFs2XTAAeSW3gIJq3RSI-R7mbgqOkdfaga+bH93kSXeu7L76DhFfc5kPbGo99KfvZpyc2XYyyp9YgPmGohn2dA6aQOoJMAIJqDMxUMdGmB3lnIIh8zt31fE7ABdX8-5AJa-OjXMsC26IXCgDsD7tORWVrEStKaTk-jrmtpspk9RWSAz1fSPbGkhwIQPsTY25F88QtL81P+DNTkRQjhjgg8ZBLGc0ESKEHJCaFc3gcRt9JitmBkxFKrYdaxZfOYiLj9haG9w-OoGU2J77d4gPISfPo+8SB1bl-Lwbe8HHhTt59YWIukmiyHJEPqZBxj+LDu03wmQnfWmB9MErCSJtpHyIPreSRkhi8QGAtGqz+yBxk45jyzX4N6xB1A6R1qf51CeJkCd48jd6sIMJFZIG2ZsCczqq-pqwYD3TXzX6cR6ADjPpPI04PgZjP4z6cAJidxojTqGbYxBwsDYzUESDNib6sHiAb59YwjqgNas7kBeRgYwhAYtpCyejeglJsBr5cG2ab456oEghMTpAPh1AMCxycEET0H1wiRMEsFcASDJBgARwhhAA", # nolint - mode = "editor" + blockr:::create_app_link( + blockr:::shinylive_links["ae-forest-plot"], + "editor", + header = FALSE ), full_screen = TRUE ) @@ -231,163 +232,8 @@ Toggle code
-```r -library(dplyr) -library(tidyr) -library(forestplot) -library(blockr.pharmaverseadam) - -# Function to create adverse event forest plot -create_ae_forest_plot <- function(data, usubjid_col, arm_col, aedecod_col, n_events) { - data <- data |> filter(.data[[arm_col]] != "Placebo") - # Convert column names to strings - usubjid_col <- as.character(substitute(usubjid_col)) - arm_col <- as.character(substitute(arm_col)) - aedecod_col <- as.character(substitute(aedecod_col)) - - # Calculate the total number of subjects in each arm - n_subjects <- data |> - select(all_of(c(usubjid_col, arm_col))) |> - distinct() |> - group_by(across(all_of(arm_col))) |> - summarise(n = n(), .groups = "drop") - - # Calculate AE frequencies and proportions - ae_summary <- data |> - group_by(across(all_of(c(arm_col, aedecod_col)))) |> - summarise(n_events = n_distinct(.data[[usubjid_col]]), .groups = "drop") |> - left_join(n_subjects, by = arm_col) |> - mutate(proportion = n_events / n) - - # Select top N most frequent AEs across all arms - top_aes <- ae_summary |> - group_by(across(all_of(aedecod_col))) |> - summarise(total_events = sum(n_events), .groups = "drop") |> - top_n(n_events, total_events) |> - pull(all_of(aedecod_col)) - - # Get unique treatment arms - arms <- unique(data[[arm_col]]) - if (length(arms) != 2) { - stop("This plot requires exactly two treatment arms.") - } - active_arm <- arms[1] - control_arm <- arms[2] - - # Filter for top AEs and calculate relative risk - ae_rr <- ae_summary |> - filter(.data[[aedecod_col]] %in% top_aes) |> - pivot_wider( - id_cols = all_of(aedecod_col), - names_from = all_of(arm_col), - values_from = c(n_events, n, proportion) - ) |> - mutate( - RR = .data[[paste0("proportion_", active_arm)]] / .data[[paste0("proportion_", control_arm)]], - lower_ci = exp(log(RR) - 1.96 * sqrt( - 1 / .data[[paste0("n_events_", active_arm)]] + - 1 / .data[[paste0("n_events_", control_arm)]] - - 1 / .data[[paste0("n_", active_arm)]] - - 1 / .data[[paste0("n_", control_arm)]] - )), - upper_ci = exp(log(RR) + 1.96 * sqrt( - 1 / .data[[paste0("n_events_", active_arm)]] + - 1 / .data[[paste0("n_events_", control_arm)]] - - 1 / .data[[paste0("n_", active_arm)]] - - 1 / .data[[paste0("n_", control_arm)]] - )) - ) - - # Prepare data for forest plot - forest_data <- ae_rr |> - mutate( - label = paste0( - .data[[aedecod_col]], " (", - .data[[paste0("n_events_", active_arm)]], "/", .data[[paste0("n_", active_arm)]], " vs ", - .data[[paste0("n_events_", control_arm)]], "/", .data[[paste0("n_", control_arm)]], ")" - ) - ) - - # Create forest plot - forestplot::forestplot( - labeltext = cbind( - forest_data$label, - sprintf("%.2f (%.2f-%.2f)", forest_data$RR, forest_data$lower_ci, forest_data$upper_ci) - ), - mean = forest_data$RR, - lower = forest_data$lower_ci, - upper = forest_data$upper_ci, - align = c("l", "r"), - graphwidth = unit(60, "mm"), - cex = 0.9, - lineheight = unit(8, "mm"), - boxsize = 0.35, - col = fpColors(box = "royalblue", line = "darkblue", summary = "royalblue"), - txt_gp = fpTxtGp(label = gpar(cex = 0.9), ticks = gpar(cex = 0.9), xlab = gpar(cex = 0.9)), - xlab = paste("Relative Risk (", active_arm, " / ", control_arm, ")"), - zero = 1, - lwd.zero = 2, - lwd.ci = 2, - xticks = c(0.5, 1, 2, 4), - grid = TRUE, - title = paste("Relative Risk of Adverse Events (", active_arm, " vs ", control_arm, ")") - ) -} - -new_forest_plot_block <- function(...) { - new_block( - fields = list( - usubjid_col = new_select_field( - "USUBJID", - function(data) colnames(data), - multiple = FALSE, - title = "Subject ID Column" - ), - arm_col = new_select_field( - "ACTARM", - function(data) colnames(data), - multiple = FALSE, - title = "Treatment Arm Column" - ), - aedecod_col = new_select_field( - "AEDECOD", - function(data) colnames(data), - multiple = FALSE, - title = "AE Term Column" - ), - n_events = new_numeric_field( - 10, - min = 5, max = 20, step = 1, - title = "Number of Top AEs to Display" - ) - ), - expr = quote({ - create_ae_forest_plot(data, .(usubjid_col), .(arm_col), .(aedecod_col), .(n_events)) - }), - class = c("adverse_event_plot_block", "plot_block"), - ... - ) -} - -# Register the custom block -register_block( - new_forest_plot_block, - name = "Adverse Event Forest Plot", - description = "Create a forest plot of adverse events comparing two treatment arms", - category = "plot", - classes = c("adverse_event_plot_block", "plot_block"), - input = "data.frame", - output = "plot" -) - -# Create the stack -clinical_trial_stack <- new_stack( - new_adam_block(selected = "adae"), - # filter_in_block(), - new_forest_plot_block() -) - -serve_stack(clinical_trial_stack) +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +blockr:::print_shinylive_r_code("ae-forest-plot") ```
@@ -753,9 +599,10 @@ In the below example, we implemented the Lorenz attractor and solve it with the ```{r, eval=TRUE, echo=FALSE} card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhBeTKwcXLyaphIMdtaooqQATAHhymoaQbr6RiaihZY2dg7Oru6e9FHVAfUhTeGR0bHxiSlpEOncLIxQDACefAWSM-OLK3yoDFAEMFCbC-s760XWJWXlM+kQcNQA+kQAJnBP6zIAPOYyAGZOCAEUjuCB8DCQ5IyEDpdIyGSieQQABePz+gOBoJIMj4pFky1kqCWMHY0NhEARCLqlKpCIAGqoZMSGKTgABGAC6MgAVDJlhzuQBqfnAcrcvkCgDMnNw8LpMgAmkyWWzxbzcQL1X9pZzknLaXSAFpM8wCrkarXC5kk9jAGWWsXcnX2znyhEzBEAXzhtP+LDg3Feyl+iJYnEEhpkUCZD2eECc8AYLAIT39gdefHMAA5DFLZOZ2QAGWTlIv690yOixx5PBNJlNpgNBrPFgsAVhLMmL5YNdIINfjibgydT6Zb5WzsnZ06L5fld1pcc+LXEkbp4+DTM37D7VLgAA89kz2C5OA4nOR1wrYhhXlAuBh-vt4NeFTI3nAACztt-vgFArIlbvssRZMhojJqDOSpMtBJpQRWUbvqQYFqCWwEKqQ-rQNwsFznu-42qyNTqBCfDHLIGBrPqMhUQQqQYR6GGIe+8wRpiTJIhQKKelSLFUpCGAEeo3CxCRdRgJ+K5mIMYD3lw0kSCkC7pD6sw5Ac4jojIy6cJp17LlJ6xAUutZXKURCkIpa6VpiA61JoB6ycsKQmXS7z-FATjcFZxDcImHDgZoywYOyzkYJUYC8TR8rLtYcBEDAihsFZ6zpDM7AjgAbh8ekSHweXiMkYBepyQA", # nolint - mode = "editor" + blockr:::create_app_link( + blockr:::shinylive_links["ode-demo"], + "editor", + header = FALSE ), full_screen = TRUE ) @@ -775,55 +622,7 @@ Toggle code
-```r -library(blockr) -library(pracma) -library(blockr.ggplot2) - -new_ode_block <- function(...) { - - lorenz <- function (t, y, parms) { - c( - X = parms[1] * y[1] + y[2] * y[3], - Y = parms[2] * (y[2] - y[3]), - Z = -y[1] * y[2] + parms[3] * y[2] - y[3] - ) - } - - fields <- list( - a = new_numeric_field(-8/3, -10, 20), - b = new_numeric_field(-10, -50, 100), - c = new_numeric_field(28, 1, 100) - ) - - new_block( - fields = fields, - expr = substitute( - as.data.frame( - ode45( - fun, - y0 = c(X = 1, Y = 1, Z = 1), - t0 = 0, - tfinal = 100, - parms = c(.(a), .(b), .(c)) - ) - ), - list(fun = lorenz) - ), - ..., - class = c("ode_block", "data_block") - ) -} - -stack <- new_stack( - new_ode_block, - # Coming from blockr.ggplot2 - new_ggplot_block( - func = c("x", "y"), - default_columns = c("y.1", "y.2") - ), - new_geompoint_block -) -serve_stack(stack) +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +blockr:::print_shinylive_r_code("ode-demo") ```
diff --git a/vignettes/blockr.Rmd b/vignettes/blockr.Rmd index 19dae60c..9e7afca7 100644 --- a/vignettes/blockr.Rmd +++ b/vignettes/blockr.Rmd @@ -34,9 +34,60 @@ note_box <- function(..., color) { knitr::include_url("https://bristolmyerssquibb.github.io/useR2024") ``` -## Get started +# Get started -`{blockr}` provides plug and play blocks which can be used to import, transform and visualize data. +`{blockr}` provides plug and play __blocks__ which can be used to import, transform and visualize data. This guide provides an entry point for end users without extensive coding skills, as well as developers. + +## {blockr} for non coders + +In that case, you likely just want to get started to create your first data pipeline. + +### The user interface + +This is what the `{blockr}` interface looks. We designed it to be like a cooking __recipie__, where blocks correspond to the different __steps__. +Within each block are some __inputs__ that allow you to change parameters. For instance, when you import the data, you may want to select a given dataset or specify a file from your computer or on a server. In figure \@ref(fig:blockr-ui), +the `airquality` dataset is imported. On the top center of the block, you can see information about the number of rows and columns. Then, a filter block is added and target the `Ozone` column. Notice the __validation__ message as the value is missing. The submit button can't be clicked as long as the input is empty, which prevents from triggering errors in downstream blocks. Each block can be collapsed/uncollapsed and removed with the corresponding buttons located on the top right corner. As you can see, we group all related blocks inside a common container, the __stack__, which can also be collapse and removed. + +```{r blockr-ui, echo=FALSE, fig.cap='User Interface Overview', fig.align = 'center', out.width='100%'} +knitr::include_graphics("figures/blockr-ui.png") +``` + +### Starting from scratch + +When you start from scratch, there are actually no blocks in the interface and you have to add them one by one. +To add a new block, you can click on the `+` icon on the stack top right corner. This opens a sidebar on the left side, where it is possible to search for blocks. To make things easier, all the suggested blocks are compatible with the current state of the pipeline. For instance, when the stack is empty, only entry point blocks are suggested, so you can import data. Then, after clicking on the block, the suggestion list changes so you can, for instance, filter data or select only a subset of columns, and more. + +```{r, echo=FALSE} +# shinylive container +card( + blockr:::create_app_link( + blockr:::shinylive_links["empty-stack"], + "app", + header = FALSE + ), + full_screen = TRUE +) +``` + +### Toward a dashboard + +What if you aim at creating a more ambitious pipeline? In that case, you don't need one cooking recipie but multiple, so you can organise your own dinner party. Below is a slightly more complex application, where you can add multiple stacks and connect them together. + +```{r, echo=FALSE} +# shinylive container +card( + blockr:::create_app_link( + blockr:::shinylive_links["workspace-kitchen-sink"], + "app", + header = FALSE + ), + full_screen = TRUE +) +``` + +## {blockr} for developers + +In the following section, we describe the building blocks behind blockr so you can start creating your blocks. ### Introduction @@ -75,7 +126,7 @@ mermaid(" ) ``` -Under the hoods, blocks are composed of __fields__. The latter are translated into shiny inputs used to convey interactivity within the block. As you can see in the below diagram, fields are combined to compute an expression, which eventually leads to a block result after evaluation. For a data block, there is no data input. +Under the hoods, blocks are composed of __fields__. The latter are translated into shiny inputs used to convey __interactivity__ within the block. As you can see in the below diagram, fields are combined to compute an __expression__, which eventually leads to a block __result__ after __evaluation__. For a data block, there is no data input. ```{r, echo=FALSE} mermaid( @@ -148,16 +199,6 @@ mermaid( ) ``` - -### The user interface - -By default, only the last __stack__ block is visible, all others are collapsed. -To preview all block you can set `options("BLOCKR_DEV" = TRUE)`. - -```{r blockr-ui, echo=FALSE, fig.cap='User Interface Overview', fig.align = 'center', out.width='100%'} -knitr::include_graphics("figures/blockr-ui.png") -``` - ### Create a stack To create a __stack__, you call `new_stack()` and feed it all the required __blocks__. @@ -165,6 +206,14 @@ Below is a simple stack providing a dataset selector and a filter operation. `se wrapper to run the corresponding shiny app. Note that with `serve_stack()`, you don't need to worry about specifying any __modules IDs__, they are automatically handled by `{blockr}`. +blocks can be instantiated either by calling their constructor name and passed to the stack: + +```r +stack <- new_stack(new_dataset_block) +``` + +Alternatively, it is possible to create block with specific parameter values, as shown below: + ```r library(blockr) library(blockr.data) @@ -177,112 +226,22 @@ stack <- new_stack( serve_stack(stack) ``` -```{r, eval=TRUE, echo=FALSE} -card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkZWQw5+YWSGAAmUFwlmdl5fKg88AyoFNZObOwl6ZxQEjIAPOYyEHDUAPpDEoIQMjJVAIyqk9MzdVzscKQzVXx73HAE5DUbQl0QPX0BbRJQ1nBXYG3cHTd3HCm46csqgAmDZTWYAMxY3HIDEOogWxG4ThgHDe7C6BBYcH8eBkADceE5sW8AII1OCZOApdIlPYMPFwOZcBbzcTJMAAXwAukA", # nolint - "app", - header = FALSE - ), - full_screen = TRUE -) -``` +By default, only the last __stack__ block is visible, all others are collapsed. +To preview all block you can set `options("BLOCKR_DEV" = TRUE)`. ```{r, eval=TRUE, echo=FALSE} note_box( "You'll notice that some blocks, such as the `filter_block` - have a submit button. The reason was to prevent any expansive task - to run without explicit user approval. That said, this feature is - still very much experimental and might change in the future.", + have a submit button. This is to prevent any expansive task + from running without explicit user approval.", color = "primary" ) ``` - -In the next example, we'll see how to create an which can dynamically add or remove blocks. - -### Dynamically add a block - -The stack exposes a helper button to add a block in the top right corner of the card header. -It triggers an [offcanvas](https://getbootstrap.com/docs/5.3/components/offcanvas/) -UI panel, that basically allows one to select any block available in the -registered blocks [vignette](https://blockr-org.github.io/blockr/articles/registry.html). - -As a side note, there's another mechanism that works to add blocks, which we describe in the -following. It is more verbose but allows you to -bring your own custom UI. On the UI side, we call `generate_ui()`, -which is nothing more than calling the stack module UI and `generate_server()` in the Shiny server function. If you leave `id` NULL, -the namespace is automatically handled. To get the list of available blocks, we leverage the `{blockr}` registry namely `available_blocks()` which exposes the -currently registered blocks. You can learn more about the registry in the dedicated -[vignette](https://blockr-org.github.io/blockr/articles/registry.html). -`add_block_ui()` is a UI helper to show a button as well -as the necessary interface to contain the new block choice. -Within an `observer` we listen to the "add block" button. Then, we pass the new block in the `new_block` slot of the `generate_server()` function. -It accepts the name of the block to insert as well as its position in the stack (here we append and leave `NULL`). - -```{r, eval=TRUE, echo=FALSE} -note_box( - "Note that `{blockr.demo}` (https://github.com/blockr-org/block.demo) - utilises `{masonry}` (https://github.com/blockr-org/masonry) to provide - a better user experience, allowing one to drag and drop blocks - within the stack.", - color = "primary" -) -``` - -```r -library(shiny) -library(blockr) - -stack <- new_stack(new_dataset_block) -shinyApp( - ui = bslib::page_fluid( - add_block_ui(), - generate_ui(stack, id = "mystack") - ), - server = function(input, output, session) { - vals <- reactiveValues(new_block = NULL) - stack <- generate_server( - stack, - id = "mystack", - new_block = reactive(vals$new_block) - ) - - observeEvent(input$add, { - block <- available_blocks()[[input$selected_block]] - # add_block expect the current stack, the block to add and its position - # (NULL is fine for the position, in that case the block will - # go at the end) - vals$new_block <- list(block = block) - }) - } -) -``` - -Blocks can be removed in any context. Keep in mind that a stack can't contain 2 data blocks. - -```{r, eval=TRUE, echo=FALSE} -note_box( - "At the moment, we don't check whether removing a block breaks the pipeline.", - color = "danger" -) -``` - -```{r, eval=TRUE, echo=FALSE} -card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkZWQw5+YWSGAAmUFwl6ZxQEjIAPOYyEHDUAPrNEnwl7HAMAG5w-VyDA+LJYAC+ALpAA", # nolint - "app", - header = FALSE - ), - full_screen = TRUE -) -``` - ### Example with modules -The stack can be nested within modules. This is what we do in -[`{blockr.demo}`](https://github.com/blockr-org/block.demo). +This might be useful if you consider embedding blockr in your own application. +The stack can be nested within modules. ```r library(shiny) @@ -328,27 +287,36 @@ shinyApp(ui, server) ### Connect stacks: the workspace The __workspace__ allows you to create more complex analysis by connecting stacks together and get a dashboard. -To know more about this you can read the following [article](https://blockr-org.github.io/blockr/articles/data-blocks.html#reading-data-from-another-stack). +To know more about this you can read the following [article](https://bristolmyerssquibb.github.io/blockr/articles/data-blocks.html#reading-data-from-another-stack). -```{r, echo=FALSE} -mermaid( - "flowchart TD - subgraph s1[Stack 1] - direction TB - input_s1(Data) - transform_s1(Transform) - input_s1 --> |data| transform_s1 - end - transform_s1 --> |data| input_s2 - subgraph s2[Stack 2] - direction TB - input_s2(Data) - transform_s2(Transform) - output_s2(Visualize) - input_s2 --> |data| transform_s2 --> |data| output_s2 +```{r, echo=FALSE, message=FALSE} +mermaid(" + flowchart TD + subgraph LR workspace[Workspace] + subgraph stack1[Stack] + direction LR + subgraph input_block[Block 1] + input(Data: dataset, browser, ...) + end + subgraph transform_block[Block 2] + transform(Transform block: filter, select ...) + end + subgraph output_block[Block 3] + output(Result/transform: plot, filter, ...) + end + input_block --> |data| transform_block --> |data| output_block + end + subgraph stack2[Stack 2] + stack1_data[Stack 1 data] --> |data| transform2[Transform] + end + stack1 --> |data| stack2 + subgraph stackn[Stack n] + stacki_data[Stack i data] --> |data| transformn[Transform] --> |data| Visualize + end + stack2 ---> |... data| stackn end ", - height = "600px" + width = "100%" ) |> htmlwidgets::onRender( "function(el, x) { diff --git a/vignettes/data-blocks.Rmd b/vignettes/data-blocks.Rmd index 126a9718..b6e3f822 100644 --- a/vignettes/data-blocks.Rmd +++ b/vignettes/data-blocks.Rmd @@ -50,32 +50,18 @@ new_dataset_block <- function( The `new_dataset_block()` function defaults to using the base R `datasets` package, but you can supply any valid package containing one or multiple data sets. Here, we use the `{blockr.data}` package to select the `"lab"` data set: -```r -# Define the custom data block -custom_data_block <- function(...) { - new_dataset_block( - selected = "lab", - package = "blockr.data", - ... - ) -} - -stack <- new_stack( - custom_data_block, - new_select_block -) -serve_stack(stack) -``` - -```{r, eval=TRUE, echo=FALSE} +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +# shinylive container card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkQBE6cRDAA+gAmUFxVheIyADzmMgBmTqWk7hB8GIPJMiDpMjIQcNS19bFwpI2iEoIQ4+PscNxwBOQ1qoFg3FB00mNrqFASUNZw+0JNDBh1XKera4MYZyUAvunpnJdmm0JlMqgDlmcyhVqs8oIszLIzpNphstjt4RJ0iUNgwAG5wMFcZbg8TJMDfAC6QA", # nolint + blockr:::create_app_link( + blockr:::shinylive_links["dataset-block"], "app", header = FALSE ), full_screen = TRUE ) + +blockr:::print_shinylive_r_code("dataset-block") ``` ```{r, eval=TRUE, echo=FALSE} @@ -101,26 +87,18 @@ of the four following data __parser__ blocks: If you want to load data from any location on your computer, `new_upload_block()` is what you need. Since the `new_upload_block()` temporarily moves data into a custom location, for security reasons, it might not be always possible. -```r -library(blockr) - -stack <- new_stack( - new_upload_block, - new_csv_block, - new_select_block -) -serve_stack(stack) -``` - -```{r, eval=TRUE, echo=FALSE} +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +# shinylive container card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkQ6ZxQEjIAPOYyEHDUAPrlEoIQMnUNjU6oolAAJo2F4rjpHfVNBOwAbkOiEqPtnU3scNxwBKRzZuklqwzTcM1crS3iyWAAvgC6QA", # nolint + blockr:::create_app_link( + blockr:::shinylive_links["upload-block"], "app", header = FALSE ), full_screen = TRUE ) + +blockr:::print_shinylive_r_code("upload-block") ``` #### Files browser block @@ -154,15 +132,18 @@ stack <- new_stack( serve_stack(stack) ``` -```{r, eval=TRUE, echo=FALSE} +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +# shinylive container card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkQ6ZxQEjIAPOYyEHDUAPrlEoIQMnUNjQBmLNxw7IxE1OxwDI2F4rjpHfVNBOwAbhOiEtPtnU2j-QSkK2bpJaMMi3DNXK0t4slgAL644NDwVKgU1k5s7BgLi3iEJORKMghOxXgQWANpEIWOxuFAIAATKHCPrcRr9CDWRyNGAwZF0VGNBEKbG45HdTLoMbot6kvF4IR0IgI3I42LsRpJBlgUYAD2RuTgOSE6SEAEFiZk4MiACqJMajUp4ADMAE4MABGXAagAcGAA7NqdVrlfqAKwABmmYBgPGleAATBaLfrRWAJXApbL5QxFdI1Rgzdr9RgACxGgBsuGVOud1u6cFt-WkTpdbo9Xu5cpsCoo0lDFowyqN2tVQeVDst8cTdpTztdSozEO9Od9ebwADkxbguz3u73e6mG+LJc2sz6-SqIwbS0XS8XlaGq0IE0n7bgh+nR+uhNnrLmldH1cWnRgoxrVVblRHlzba4761vPWPrXuD-69argxgdUaTRHK2rNc6zTRttxbfc20PAMHVnc8y1wUMI3Na1gIfUCR2fHcwDfKD-VDTUjUIi8F1DFDB0fMCsIg988FDWDT1gi8rXoqsKIw91wPHVtJ2jENfw1EMtV1KNlWVON2OHTjqO4yDeNNH9v2LXUr31CTu03KjM1fCd20QjViJDc8dVgis4xXGtk3QqSm2w3D5L1KMHQMpjVRNWMrSENCN0ozDtN3XToIIpyDOE1Vf1DAtPLvKyfI42yaLwqcz2-ASdXLNTotXe84psridJ4vSYxnC9S3LJcsss9dNL8l8AsKw96MDHyStVQ0Kui7yauk-ycMC-CwyI8NdXDMTAIsrrfJ6uq+oa-MQuai9w3o8yYuqqaEu5AAhGFiHXBS0rnQTRqioCcu6zbrR29g9v9ENDV1ErY2jCNVsm+L8qEa7bpVM0MC-C8MCYnUvxjVbstii7PrAb6iH2vVgeI9Kj1vd68pkq7drhj9FMEoGjSvDzUPOjbodh-a-uUz8jUNMHKrRp9evJ-NC2MlLdQXM1UZJj6Ma+rH1wLRaQwBnVadTM7IdJvmYYFu7-qI899VMjVbwh9beaZuW6MLINHtFq8y06nn0d6gAReRYH9dU9enB79V-Cs1aqkDTZmi2hXpPj8ce4SHaPN6TcZ93La9gM9f4o1HfE+mg60kPPZZhX9aNUbL2NqXNYTq3kuGh6ELGoMJrj2rsI9nOj3x5ziNVKNQ1VjONbdsvQ5x38nVLQ3ucz5vkXLr2msRvXnuVLnY574OW8TlUjNnKmTRjyWm8nvvW5VdUBPVYSdWWm9G9dlfuX7-1p2Ho0Vedhn46niuhcO4aC-T4mJ+v1fp5eojh9EjVwZd6zD+tMfOiBEwqtTrqdLyJdpo3zDg9acotyzOnHsvV+R816VzZpvFir197-1QYA9B9ca7n2jKxZ+KDS5vwrgpYeyd9RfgdG1IMkkAFCCAR-PGANLykMvlAy6bDCEOjnNXVyWp67dwodA5EzN17s3upGaMlpkEgR1Kw2WN1sY6yRsnHUEVF6QMzqo-B-MNEU2-Fwq8FUl4qLUTIxCrlmoSwLBImxxj1E-VIYtPU29DSMN-u9IxlDtra30oNR68ETSZXIa4oJmNTHW2-F-RRvDDG2JCULTB85oxEwMRrQJUjgnxOSrbdmxoNzpWUehfJ-D3GaO9vBGuo1zS4LitUsmITR4lXgUacsDdrFVLSUU0JJ5QplUQhAtaMSClxI8QpJWPSXr+J5m0mWdiiHbx9qqWCziWmphWVrIZ14wlCVTuUlJeTBkeKIcZUspkjbRIGW4uxnSSmuXLKPSprTLl1PWbObeEUf67OdPsma9kip-QBtOeCV4LTnKmTUsFjUDKbzCeFcZLjHmxPqnJIqypFoWMWZ8vZajEXWyeqiw2gdUluNJbIpWmzRJj36V8ml-U6J-UOlw9qfTcnwuhrSxRwt8WqSWdSrFs0cWNSEYdPWCFWJAotCCuybLEKFkhbjNyfFRUXNZXNGemywk7wDgqpViV5LTiYtOHxySiXApJSqpqoCwpBiiia+1erPH0uBqDCWxcxXTOxbRexdD2a10QlE3lmKA0SqDUch6eo0q0xyWAdWfKZYCs6aAh+xYVpurcewhSylI4XgXnCqNNT2FEJPIWZSqpRpMsjSy8VBbpy1uFdkqlOrm3oKOaLUpoMlHMuJfmnteo23epRnm7t79ip9qwYSoddqR0zr+vGnypkG0pr-k26NlbRm+x8i6-Rkzy3QwLXnJG5VtVpvNj29UbNt7gIxTuitPaLVKRptGY9qbT0y0rYWTlPk9GdpvdnMO3jFady1baxVaj-1HT1LKuuHyHkvrPT2vFwkrXBnbk-P1Xbd2ELxUPW5+kQO-oObMiOyctl8XGlu96qpvmCy1B3J0OblZFxPXFJjTyOnyI1MZBcLouM-p48x+W7crTMXsc+1MvHxXPJFpBkiG4tmie3fJiTsj406PrXJ50Cno3PMct+VyoNmmLotEZmpJmq7Se4Wqcj4m+OHIg4JP80ZBIacY9pxCxHSylnavqFCjatOudme50+pZHYmR8zzGz7ShlLhSqeWtYicGofC4pjp6p7atQXL6hjCW-OD1nLK9qDpQvcey8Z3LqXCz5xNHh4rmdEurPSUI3Tta64iay4Z91kqcbzN1Lh696F2u9QzSGat+KLPVd87qobv0SrxtBih-DIFJugodfu0zup1v0cW+KjN768bA1pjy1rGttvKo9QpfOGrHaDrCwNpbQahZMXtqJKxm2JuDY+wZYaiH13ZIWyV97SVBVMQhQo8SZaXMnYdazQLF4vyuv69ZgHUPioCVniW6MVX4ttex7xIhtDa1Huc7VhFKq1TfjCiaWFMHbtmr0pFJGG4f6qup29pH92QecKg4XKzrPZIfcw7OVytMXs1b54Rmd04ZVZI+SzuDPaQy0NlejsiXHjsK+oe5-HYtSH0bEzT9D79fl41lZShVYuCErrkfik03nRfq6t4WBihZWNxjVODkny7qFwK8VetXQew55fMVk+VmOHcCM9z7E5qnFzjcRwbgeKPzufsXAHm7HvqFCOEn9Yejtmfu4j0ne2wvONx4L7Ao6SvSMx3D9O6hfb43lj3nXyvsjEbQtk-b+vJ9SmGpOtV838vX2K9SmMxcCOLd-o17jdzqoFyWde1j3vHDk8m9NGnxft6rcGS1z5MRSCe9CAAOIUFIEQIg0j0nYY1AF5yYjZeT44jfsg9-H9DKrIJsIuJLgGaBGnLl-rfr-oUlcnqA9CAlzixL9tdgfEqN-nfg-tAXUgAX9AxLqCAWAVfNfpARgTMj8vjgREGM5EGGaBMoQWAGgVAaQYLNFi-o4tzkuAvlNAwSQSYlcn9MNMFFzmIsmp-lJNwX-lcnbNqJTFzl+GaEVnQeIZgYLHispHiuGE6DrgflwcQRIT8k3qrINFQSAVdoobocoUnADHir4hqLvJwRAT-jwbUoLOqqxoJiAZumYY4XoYLCXtqNYVznYTBmIeYUwfmHqAIali-iARUpjiEd4RYeyrjIIc5CxEVqIW6EoWEXRMeNITOM5IaGaLEZvvEegT4fmExHipQewVdhkagaEbwT8uqDDlXM5LgBGMel4WUYkYhGdhQT5OjiIZpjoQkdkYhLAXkaxg6LBGaBtuAaUYwY0euJaD7ARCMr+KAbzg4d0WMSlsJP0cYUosEZkQ0c4fNItAcdEZFNodsYsWcXRK2nke3KrCAeXiUScaMUsRUcnJLocRfn9rlB8TsV8Q8cRH9G4dQYCnEUCXcWsv0bYcIu4fXOpICfUZ8fceGrjNUdQd3u8WicCRiaGAjNqGsQgb0TcQsU4XYtgcIh3FzAZrcVSekjNnkSeNMSAZWAqpSeUXRNKiSfZujmAXUUQeiWsuCfyZQS6K8fYdyT0WaPesGBuMAa9AybKbsVvPye3HSbUcMbBvibCekhEdqI8dMVaDQVsfklkSCcGueAEc5DmvcgCcOiKQSXCccoiXIZyZjpaacXCf4Q1novYT6aKYaSVKukBiAXMQEjCUycll1v6b4oqpOlZsGa6ekgqe4dMdQQQcsjGTyYhDIawTYSxN+rqamQaclhmdUXXBvuAeWbGR4isXrLDtMTMZ4bmfqQ2T8niraQrAUYhC1p-vWfmSlnrHigwuwekWWXmXKbrHkQwoBOaVOvQb6ekgRFYYicWGaFCU6Uui6RWXwT7BylztQZ0R2fuV2cwezARLgSxMUcgVGlaYSYLvAaaZGRaTObseKQiRoewRMkOZ+daSscXhuIuf8XWYBYSSwQRL+e1IOdOZ2SOc2UYXSW8RBYhT0aGHigJOORGVhTccOZhX4S8W+Ucd6ZBWsgFgiZKRFEgQBRhWMSscNDIdMeGPScuU+WKUdNhXhVOdGQxdaUhKsX2W0bMR+QJYSeGawROctP+QhRefmTQUdOGQ6MqFqGaLiehQpURckVXKVGRARRRembjE3qpWaTmf6pxU-sJb+UEX2DulZZWYtO+tMXIcmvxdpbsVUfyQxB3AZTBoRYxY1tIaBVue2ZZauclgJtWeGkGUZcltOMxalqJeBR5SuSGclsSYYU5KJTuVpelWmQlRcYpP2fIYZRJWsolSFc5DWeVZ5YJftk5MAUUeJfVYSZrj5QMeSQFfFY2fuiaR3DghxZFYeZUSVS8brj1RVekjgcaShVuY6flY5VchqdRVztsgBMTvntNf-kIshTlepUgfrstWQWEikRqNsrXruVvm1dSdJi2Q6GxVGRDrdWuT8eNZdfYUZidcsQBh+tMRsZlpvt9SNd2WdUYXgUhDcSDRlX1UdDeVzhsWeYHq9VFRceteMRPrqTDYVY2UXoqWZR4QyTjQeadc2YjQOV9b1VgUIl9oRADSAbQS9QVaTb4cVdUZ9a3izZefmDbHNVMWxZpcdaDYLPCQRDYctPeZ-iTTzXgCsQJCpQ6FGGVUPjtStWEseTVQOUdszT9ecQDCkeyUSVjcLbDT8kaT+T5DmptRXqjXDVho4ouV6cDdTYLILtUYMVTWrVgbDh5gzRpVsTLSOSGC0diQOSbbrSLbzWCaBRsbLqbbjadWoeNTWYBOxEHZhQYdhocfHZHWbSxiVOLWSf5bbdzYpeoU8bIR4YHa7fmKoSSXhaAVtVtrXXRO5uKZodXarXbVgXOYYbgcreBdLa3QOViSJXIYtQnazXXYtLNfab0V7T3csSfnNSeM1bnSjWXZncJW4e1KaM3f9t7csd+U1WafeVPbLeMrPfkR5sbcwhpPLnrW3SZa0bBENZflvbsSHQmYEdrQfenk-fZQOP2CAwA1HaCQJKScYUSYvZ-UBYWElb4uyU3d3XA5JcJc8TMXxXnYnS4cnNndEUuR-QAMJaCfCkAMBoBUIDwsHKYXgboUluikPkOUOoDUPSAAF80XiiQtZ0HMMcAUNUNoLvzblHTNEq5C1QL8OcCsPsNJHA5PTPZm66lSTSOCNsPCMVzyGtQaq0xXXzFMNkMCOyOaMDyzV4ybyOyLXClgBqMmOO63zP5ZX+xOzHFKh2NCMONeyiNUybJ6OqmGMsOeMJ6ONERcIuo6l8NGMyPBNgDsI+OzhcKOwWWSIeMaNePnH0qbwLhDFRNBPpMhPeMGSJliNNJ54oFCBpNyPhofq6gZSMPuPRPqPVPyFES+7n4BONP5PVMpYAzJ6OZN1WaqNNP2OFMcN90KlOgdNcmBPGOxPxM1o+RBZfpbHDPdOmMcN44r6RINOVMjPzOEJVXhLC50UqOzMxMFNxOELiOPRvLjKrPnPNMbO8nunzwLrXVrNzOXOVqC4dUE6p4zNdNfM9NUVQqfp+JuN7PrMZNy1Z7qjZodqAtQvAvPO9GrY0bvK7O2P7PfPoKtOlQXjcqdPIsXMtN91GkSwAspmPOjNXNW5cMMJ+7uXnnYvQtjOgkXqbw7NTUktPMwscklVWhOgurhUEZVOotCzqrGTix1Wssov8tKXe6CtiLI1is4s9N7VzXxq0xzH0W8u0vxMbJSY5p5VpXiv8uhhR7HMXhd6tVyuksSt-MGGqigzwVmvquOuj6U7ZJxVAsOsKtKvFnjF2vmvstotSukZVayuht0taPeWAyMsishsesWtcJsFdzLkxuVpnZ-N76aV6v2t8thsrFpthSMoMn5JZuELEX5yMrRspvFtqrR7cMvQR2b1VsiN-WPSSliJHXM0dtaM+7NuOZyX64DsDxpshTbKqst1+tFuxtFNeJPTizE00sHP0u4wskXiOzIT-21bjvhHERHMutfrKNjsNsLscNA6BZTPJku1zsGvVvou1oLhA0Pnp4Huws6OJk5poXntsuXsvPUZUw6uwOfsCuIyby0x9vtsXuVrmNgv-PO3vv7twfXNiN9kWi-ursPvruDs+wJqg50wkNodW5-QNIPz1pnv9ukdaMGRpuSk8M4f6t4c0Mv3Dygz5vY1ru4tkerZz5cf-vyvFvHmAxSbtSpU0cAeVrxsQZOimSjtSfCeAej1syuTr7lOH0se8f4ebzF2mtKf+uNsEetTJNtvbV3AAC6QAA", # nolint + blockr:::create_app_link( + blockr:::shinylive_links["filesbrowser-block"], "app", header = FALSE ), full_screen = TRUE ) + +blockr:::print_shinylive_r_code("filesbrowser-block") ``` Note that in a later release, we plan to merge `filesbrowser_block()` and `upload_block()` into @@ -176,28 +157,16 @@ a `new_dataset_block()` from which we can select some columns with `new_select_b we can reuse this smaller dataset. If you dynamically add a third stack, you can also select the third stack's output as input to the second stack (it appears in the result block select input). -```r -library(blockr) -library(blockr.data) - -serve_workspace( - stack1 = new_stack( - new_dataset_block("lab", "blockr.data"), - new_select_block(c("STUDYID", "USUBJID")) - ), - stack2 = new_stack(new_result_block), - stack3 = new_stack(new_dataset_block("ae", "blockr.data")), - title = "My workspace" -) -``` - -```{r, eval=TRUE, echo=FALSE} +```{r, results="asis", echo=FALSE, warning=FALSE, comment = ""} +# shinylive container card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDcLIxQDACefKYSDGkZWQw5+YWSGAAmUFwl6exwDABucAD61Fbi7KhQBHCCEDIynAPiAIyqMhBw1B3jEsOjo3MLdVzNpB1VmtxQdAFCVQy19VApuOmrs-OLcNxwBDt7GkIAygAqAKoAIgBNACSf2OYB+Hx+ACEAFIglIlUbJa4jMZcCQAJhm60W6PEfBx8nYTm4r1EEmRNzREwAzNj7kt8TjNrE4GSzJooHAwadzlwESjRqQHE8ZkIALK5GTdBi9fqDITpZJgAC+AF0gA", # nolint + blockr:::create_app_link( + blockr:::shinylive_links["result-block"], "app", header = FALSE ), full_screen = TRUE ) + +blockr:::print_shinylive_r_code("result-block") ``` diff --git a/vignettes/developer-guide.Rmd b/vignettes/internals.Rmd similarity index 98% rename from vignettes/developer-guide.Rmd rename to vignettes/internals.Rmd index 6be8b159..65072a57 100644 --- a/vignettes/developer-guide.Rmd +++ b/vignettes/internals.Rmd @@ -1,8 +1,8 @@ --- -title: "Developer guide" +title: "6: Internals" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Developer guide} + %\VignetteIndexEntry{6: Internals} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- diff --git a/vignettes/new-field.Rmd b/vignettes/new-field.Rmd index 17f84ebd..ac59816c 100644 --- a/vignettes/new-field.Rmd +++ b/vignettes/new-field.Rmd @@ -108,11 +108,13 @@ ui_update.slider_field <- function(x, session, id, name) { We can test our newly created field in a custom block. -```{r, eval=TRUE, echo=FALSE} +```{r, echo=FALSE} +# shinylive container card( - create_app_link( - "NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAdzgCMAnRRASwgGdSoAbbgCgA6YOtyIEA1gyG4ABAzioi7GQF4ZBQWAAWpUqnaIA9IZFjJAWiIMA5hmstSWgK50MLIodqNz8xe2kyQjp6Bsa+RBgM5tRQ7DAYVtZCAJTJAhDp3CyMUAwAnnymEgxpGRAQcNQA+uxZACZwDFUAZixw3HUyADzmMs1OEASk7hCCEDITMgBuPE5wqjIQTvAMLBrJuOmTMjBsC0sra3wbW5MwUAAe+8uNRyfjk5wK14frmw8TGF-JMiCnRZJmBVqq12nUxtsZtw5gsoXN3ttduM1EiEWdLgtzhc0RMnqgFnicTIvhgiQRuLFlGohLUWA0mqCOkJTqUAL7pdJOFhVNioJykDC0+ktNodbq9fqDYYkPjYmR02TQeA-P4PdhaNh5ZhCxoASQgfNIEMmAKYrAN-J5dXYstkdPu2yVcCJcPmalNzFdtsCYFdKSJSIWHsQXrlQiR-tOEyxQdExU9szg3vDl0jHxkeNjZjNodkNPIqBSLPS7LKULpUHIIrBgvqjWrYp6fQGQxGspVpyh4umifbp0DTdzPojYFK0Yxg97YbAWKLaoL3aH+YUc87PArVYOdEafChpVOLGaMj43Ao1kcfCRqV+UZ79UrcCqW53qOHbDnE1LB6PJ7PF6x16qpC651A+T7LNuDCXpceYzqmo6nF+DyHsep4QOeWh8HigG3uWoGbhBO6Ej6eIfjISETAAcnAFykAAsnAjhEOCbIcks3JOKg+FwLWdL1oynRNpKrYynK7BwOw7AjHadSKrAcAdg8nHcQAynWDD6oaxq4hJUkkESwa8padI2nK9pEk6BlxoCIZTg6JrWTmU6wSO9kTMGS5wRcabbB5zkkQWZGsWU8j2E8DAqQAzPATHgkIXI8hapABDS6kNnUAQJUZAo6gyop1KUoUsOFUUxVozGaAlykPilYC5elmUcVxD68cKAn7uUlQ1FkBCPgC3bCdKozNAwRAwAsAAMsgkoppwQFUo3UMoTYCAIfD4VAPwQIt62VptbETAJy29FknDaXIRBLfsXX1QJ50TK6CwjWNRLRnsahTbe45XGo82Lewr0ZguagAIyA8MpCngsQgqe0cBDBdS3MumY4yB1EzAlUALnUdT35QDt40agDALAAjk4RDkOtqDcHkZq0r1WFwKTVRoXwGB8P9qSpBZ8nQ2Aal8STDPzAC0i3iSZIUpJCwaKlax9dZtWkAwUAcM0VgwFjSsIQ8wXpOJDBTI+nBQBIxqY6b5u3pjG3iaQ2tmJoLCrP4o4WTdPWK07ACsqOlMkYCsgAukAA", # nolint - mode = "editor" + blockr:::create_app_link( + blockr:::shinylive_links["custom-field"], + "app", + header = FALSE ), full_screen = TRUE ) @@ -122,7 +124,7 @@ card(