├── srcjs ├── config │ ├── misc.json │ ├── output_path.json │ ├── entry_points.json │ ├── externals.json │ └── loaders.json ├── modules │ ├── message.js │ ├── io.js │ ├── plot.js │ └── database.js └── index.js ├── inst ├── app │ └── www │ │ ├── favicon.ico │ │ ├── 266ce9b2f14064f8.js.LICENSE.txt │ │ └── fed6716264ed3a49.js.LICENSE.txt └── golem-config.yml ├── R ├── _disable_autoload.R ├── utils_helpers.R ├── serializer.R ├── hello.R ├── run_app.R ├── app_server.R ├── app_config.R └── app_ui.R ├── tests ├── testthat │ └── test-utils_helpers.R └── testthat.R ├── webpack.prod.js ├── man ├── hello.Rd └── run_app.Rd ├── webpack.dev.js ├── NAMESPACE ├── app.R ├── dev ├── run_dev.R ├── 03_deploy.R ├── 01_start.R └── 02_dev.R ├── DESCRIPTION ├── .gitignore ├── package.json └── webpack.common.js /srcjs/config/misc.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /srcjs/config/output_path.json: -------------------------------------------------------------------------------- 1 | "./inst/app/www" 2 | -------------------------------------------------------------------------------- /srcjs/config/entry_points.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "./srcjs/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /srcjs/config/externals.json: -------------------------------------------------------------------------------- 1 | { 2 | "shiny": "Shiny", 3 | "jquery": "jQuery" 4 | } 5 | -------------------------------------------------------------------------------- /inst/app/www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joekirincic/app02/HEAD/inst/app/www/favicon.ico -------------------------------------------------------------------------------- /R/_disable_autoload.R: -------------------------------------------------------------------------------- 1 | # Disabling shiny autoload 2 | 3 | # See ?shiny::loadSupport for more information 4 | -------------------------------------------------------------------------------- /srcjs/modules/message.js: -------------------------------------------------------------------------------- 1 | const message = (msg) => { 2 | alert(msg); 3 | } 4 | 5 | export { message }; 6 | -------------------------------------------------------------------------------- /tests/testthat/test-utils_helpers.R: -------------------------------------------------------------------------------- 1 | test_that("multiplication works", { 2 | expect_equal(2 * 2, 4) 3 | }) 4 | -------------------------------------------------------------------------------- /srcjs/config/loaders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test": ".*\\.wasm$", 4 | "type": "asset/resource" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | }); 7 | -------------------------------------------------------------------------------- /man/hello.Rd: -------------------------------------------------------------------------------- 1 | \name{hello} 2 | \alias{hello} 3 | \title{Hello, World!} 4 | \usage{ 5 | hello() 6 | } 7 | \description{ 8 | Prints 'Hello, world!'. 9 | } 10 | \examples{ 11 | hello() 12 | } 13 | -------------------------------------------------------------------------------- /inst/golem-config.yml: -------------------------------------------------------------------------------- 1 | default: 2 | golem_name: app02 3 | golem_version: 0.0.0.9000 4 | app_prod: no 5 | 6 | production: 7 | app_prod: yes 8 | 9 | dev: 10 | golem_wd: !expr golem::pkg_path() 11 | 12 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'development', 6 | devtool: 'inline-source-map' 7 | }); 8 | -------------------------------------------------------------------------------- /inst/app/www/266ce9b2f14064f8.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * [js-sha256]{@link https://github.com/emn178/js-sha256} 3 | * 4 | * @version 0.9.0 5 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 6 | * @copyright Chen, Yi-Cyuan 2014-2017 7 | * @license MIT 8 | */ 9 | -------------------------------------------------------------------------------- /inst/app/www/fed6716264ed3a49.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * [js-sha256]{@link https://github.com/emn178/js-sha256} 3 | * 4 | * @version 0.9.0 5 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 6 | * @copyright Chen, Yi-Cyuan 2014-2017 7 | * @license MIT 8 | */ 9 | -------------------------------------------------------------------------------- /R/utils_helpers.R: -------------------------------------------------------------------------------- 1 | #' helpers 2 | #' 3 | #' @description A utils function 4 | #' 5 | #' @return The return value, if any, from executing the utility. 6 | #' 7 | #' @noRd 8 | 9 | get_choices <- function(){ 10 | choices <- c("select", "sales", "quantity", "discount", "profit") 11 | return(choices) 12 | } 13 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(run_app) 4 | import(shiny) 5 | importFrom(golem,activate_js) 6 | importFrom(golem,add_resource_path) 7 | importFrom(golem,bundle_resources) 8 | importFrom(golem,favicon) 9 | importFrom(golem,with_golem_options) 10 | importFrom(shiny,shinyApp) 11 | -------------------------------------------------------------------------------- /app.R: -------------------------------------------------------------------------------- 1 | # Launch the ShinyApp (Do not remove this comment) 2 | # To deploy, run: rsconnect::deployApp() 3 | # Or use the blue button on top of this file 4 | 5 | pkgload::load_all(export_all = FALSE,helpers = FALSE,attach_testthat = FALSE) 6 | options( "golem.app.prod" = TRUE) 7 | app02::run_app() # add parameters here (if any) 8 | -------------------------------------------------------------------------------- /R/serializer.R: -------------------------------------------------------------------------------- 1 | 2 | serialize_arrow <- function(data){ 3 | sink <- arrow::BufferOutputStream$create() 4 | arrow::write_ipc_stream(data, sink) 5 | out <- sink$finish()$data() |> memCompress(from = _, type = "gzip") 6 | return(out) 7 | } 8 | 9 | serialize_json <- function(data){ 10 | memCompress( 11 | jsonlite::toJSON(data), 12 | type = "gzip" 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/tests.html 7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files 8 | 9 | library(testthat) 10 | library(jeb01) 11 | 12 | test_check("jeb01") 13 | -------------------------------------------------------------------------------- /dev/run_dev.R: -------------------------------------------------------------------------------- 1 | # Set options here 2 | options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode 3 | 4 | # Comment this if you don't want the app to be served on a random port 5 | options(shiny.port = httpuv::randomPort()) 6 | 7 | # Detach all loaded packages and clean your environment 8 | golem::detach_all_attached() 9 | # rm(list=ls(all.names = TRUE)) 10 | 11 | # Document and reload your package 12 | golem::document_and_reload() 13 | 14 | # Run the application 15 | run_app() 16 | -------------------------------------------------------------------------------- /R/hello.R: -------------------------------------------------------------------------------- 1 | # Hello, world! 2 | # 3 | # This is an example function named 'hello' 4 | # which prints 'Hello, world!'. 5 | # 6 | # You can learn more about package authoring with RStudio at: 7 | # 8 | # http://r-pkgs.had.co.nz/ 9 | # 10 | # Some useful keyboard shortcuts for package authoring: 11 | # 12 | # Install Package: 'Cmd + Shift + B' 13 | # Check Package: 'Cmd + Shift + E' 14 | # Test Package: 'Cmd + Shift + T' 15 | 16 | hello <- function() { 17 | print("Hello, world!") 18 | } 19 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: app02 2 | Title: An Amazing Shiny App 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "Joe", 6 | family = "Kirincic", 7 | role = c("aut", "cre"), 8 | email = "jdkirincic@gmail.com") 9 | Description: This app demonstrates the use of DuckDB-WASM and Plot.js for interactive plotting in Shiny. 10 | License: MIT + file LICENSE 11 | Imports: 12 | arrow, 13 | config (>= 0.3.1), 14 | ggplot2, 15 | golem (>= 0.4.0), 16 | pkgload, 17 | shiny (>= 1.7.4) 18 | Encoding: UTF-8 19 | LazyData: true 20 | RoxygenNote: 7.1.1 21 | Suggests: 22 | testthat (>= 3.0.0) 23 | Config/testthat/edition: 3 24 | -------------------------------------------------------------------------------- /R/run_app.R: -------------------------------------------------------------------------------- 1 | #' Run the Shiny Application 2 | #' 3 | #' @param ... arguments to pass to golem_opts. 4 | #' See `?golem::get_golem_options` for more details. 5 | #' @inheritParams shiny::shinyApp 6 | #' 7 | #' @export 8 | #' @importFrom shiny shinyApp 9 | #' @importFrom golem with_golem_options 10 | run_app <- function( 11 | onStart = NULL, 12 | options = list(), 13 | enableBookmarking = NULL, 14 | uiPattern = "/", 15 | ... 16 | ) { 17 | with_golem_options( 18 | app = shinyApp( 19 | ui = app_ui, 20 | server = app_server, 21 | onStart = onStart, 22 | options = options, 23 | enableBookmarking = enableBookmarking, 24 | uiPattern = uiPattern 25 | ), 26 | golem_opts = list(...) 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /srcjs/modules/io.js: -------------------------------------------------------------------------------- 1 | 2 | import * as pako from 'pako'; 3 | 4 | function decompress_gzip_json(x){ 5 | const gzipData = window.atob(x); 6 | const gzipArray = new Uint8Array(gzipData.length); 7 | for (let i = 0; i < gzipData.length; i++) { 8 | gzipArray[i] = gzipData.charCodeAt(i); 9 | } 10 | const jsonBuffer = pako.inflate(gzipArray); 11 | const jsonString = new TextDecoder().decode(jsonBuffer); 12 | return JSON.parse(jsonString); 13 | }; 14 | 15 | function decompress_gzip_arrow(x){ 16 | const gzipData = window.atob(x); 17 | const gzipArray = new Uint8Array(gzipData.length); 18 | for (let i = 0; i < gzipData.length; i++) { 19 | gzipArray[i] = gzipData.charCodeAt(i); 20 | } 21 | const arrowBuffer = pako.inflateRaw(gzipArray); 22 | return arrowBuffer; 23 | }; 24 | 25 | export { 26 | decompress_gzip_json, 27 | decompress_gzip_arrow 28 | }; 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules 3 | .DS_Store 4 | 5 | # History files 6 | .Rhistory 7 | .Rapp.history 8 | 9 | # Session Data files 10 | .RData 11 | .RDataTmp 12 | 13 | # User-specific files 14 | .Ruserdata 15 | 16 | # Example code in package build process 17 | *-Ex.R 18 | 19 | # Output files from R CMD build 20 | /*.tar.gz 21 | 22 | # Output files from R CMD check 23 | /*.Rcheck/ 24 | 25 | # RStudio files 26 | .Rproj.user/ 27 | 28 | # produced vignettes 29 | vignettes/*.html 30 | vignettes/*.pdf 31 | 32 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 33 | .httr-oauth 34 | 35 | # knitr and R markdown default cache directories 36 | *_cache/ 37 | /cache/ 38 | 39 | # Temporary files created by R markdown 40 | *.utf8.md 41 | *.knit.md 42 | 43 | # R Environment Variables 44 | .Renviron 45 | 46 | # pkgdown site 47 | docs/ 48 | 49 | # translation temp files 50 | po/*~ 51 | 52 | # RStudio Connect folder 53 | rsconnect/ 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app02", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "man": "man" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "none": "webpack --config webpack.dev.js --mode=none", 12 | "development": "webpack --config webpack.dev.js", 13 | "production": "webpack --config webpack.prod.js", 14 | "watch": "webpack --config webpack.dev.js -d --watch" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "optimize-wasm-webpack-plugin": "^1.0.12", 21 | "pako": "^2.1.0", 22 | "webpack": "^5.77.0", 23 | "webpack-cli": "^5.0.1", 24 | "webpack-merge": "^5.8.0" 25 | }, 26 | "dependencies": { 27 | "@duckdb/duckdb-wasm": "^1.24.0", 28 | "@observablehq/plot": "^0.6.4", 29 | "apache-arrow": "^11.0.0", 30 | "arquero": "^5.1.0", 31 | "chart.js": "^4.2.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /R/app_server.R: -------------------------------------------------------------------------------- 1 | #' The application server-side 2 | #' 3 | #' @param input,output,session Internal parameters for {shiny}. 4 | #' DO NOT REMOVE. 5 | #' @import shiny 6 | #' @noRd 7 | app_server <- function(input, output, session) { 8 | # Your application server logic 9 | # dataset <- reactive({ 10 | # req(input$company_filter, input$segment_filter) 11 | # arrow::read_parquet(file = here::here("data", "superstore_2022.parquet")) |> 12 | # serialize_json() 13 | # }) |> 14 | # bindCache(input$company_filter, input$segment_filter, cache = "app") 15 | dataset <- reactive({ 16 | arrow::read_parquet(file = here::here("data", "superstore_2022.parquet")) |> 17 | serialize_json() 18 | }) |> 19 | bindCache(input$company_filter) 20 | 21 | observeEvent(input[["send-arrow-btn"]], { 22 | # session$sendBinaryMessage( 23 | # type = "send-arrow-data", 24 | # serialize_arrow(dataset) 25 | # ) 26 | session$sendCustomMessage( 27 | type = "send-arrow-data", { 28 | # begin <- Sys.time() 29 | dataset() 30 | # end <- Sys.time() 31 | # print(end - begin) 32 | } 33 | # serialize_json(arrow::read_parquet(file = here::here("data", "superstore_2022.parquet"))) 34 | ) 35 | }) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /R/app_config.R: -------------------------------------------------------------------------------- 1 | #' Access files in the current app 2 | #' 3 | #' NOTE: If you manually change your package name in the DESCRIPTION, 4 | #' don't forget to change it here too, and in the config file. 5 | #' For a safer name change mechanism, use the `golem::set_golem_name()` function. 6 | #' 7 | #' @param ... character vectors, specifying subdirectory and file(s) 8 | #' within your package. The default, none, returns the root of the app. 9 | #' 10 | #' @noRd 11 | app_sys <- function(...) { 12 | system.file(..., package = "app02") 13 | } 14 | 15 | 16 | #' Read App Config 17 | #' 18 | #' @param value Value to retrieve from the config file. 19 | #' @param config GOLEM_CONFIG_ACTIVE value. If unset, R_CONFIG_ACTIVE. 20 | #' If unset, "default". 21 | #' @param use_parent Logical, scan the parent directory for config file. 22 | #' @param file Location of the config file 23 | #' 24 | #' @noRd 25 | get_golem_config <- function( 26 | value, 27 | config = Sys.getenv( 28 | "GOLEM_CONFIG_ACTIVE", 29 | Sys.getenv( 30 | "R_CONFIG_ACTIVE", 31 | "default" 32 | ) 33 | ), 34 | use_parent = TRUE, 35 | # Modify this if your config file is somewhere else 36 | file = app_sys("golem-config.yml") 37 | ) { 38 | config::get( 39 | value = value, 40 | config = config, 41 | file = file, 42 | use_parent = use_parent 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /srcjs/index.js: -------------------------------------------------------------------------------- 1 | import { message } from './modules/message.js'; 2 | import { init_db, initDBFromCDN, create_table, create_table2 } from './modules/database.js'; 3 | import { create_density_plot, updatePlot } from './modules/plot.js'; 4 | import { tableFromJSON } from 'apache-arrow'; 5 | import { decompress_gzip_json } from './modules/io.js'; 6 | import 'shiny'; 7 | 8 | window.db = await initDBFromCDN(); 9 | window.db_con = await window.db.connect(); 10 | 11 | var plot01_updater = await updatePlot("plot_01_container"); 12 | var plot02_updater = await updatePlot("plot_02_container"); 13 | 14 | // Add event listeners to all the widgets in each 15 | // interactive plot. 16 | $(function(){ 17 | // Gather all select inputs for x and y 18 | // ... 19 | // Add event listener to each element in array using forEach. 20 | document.getElementById("plot_01_container").getElementsByTagName("select").forEach((x) => { 21 | $("#"+x.id).on('change', (e) => { plot01_updater(e.target, window.db_con); }); 22 | }) 23 | document.getElementById("plot_02_container").getElementsByTagName("select").forEach((x) => { 24 | $("#"+x.id).on('change', (e) => { plot02_updater(e.target, window.db_con); }); 25 | }) 26 | 27 | }); 28 | 29 | Shiny.addCustomMessageHandler('send-arrow-data', (msg) => { 30 | window.payload = msg; 31 | let data = tableFromJSON(decompress_gzip_json(msg)); 32 | create_table2(window.db_con, data, "payload"); 33 | }) 34 | -------------------------------------------------------------------------------- /man/run_app.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/run_app.R 3 | \name{run_app} 4 | \alias{run_app} 5 | \title{Run the Shiny Application} 6 | \usage{ 7 | run_app( 8 | onStart = NULL, 9 | options = list(), 10 | enableBookmarking = NULL, 11 | uiPattern = "/", 12 | ... 13 | ) 14 | } 15 | \arguments{ 16 | \item{onStart}{A function that will be called before the app is actually run. 17 | This is only needed for \code{shinyAppObj}, since in the \code{shinyAppDir} 18 | case, a \code{global.R} file can be used for this purpose.} 19 | 20 | \item{options}{Named options that should be passed to the \code{runApp} call 21 | (these can be any of the following: "port", "launch.browser", "host", "quiet", 22 | "display.mode" and "test.mode"). You can also specify \code{width} and 23 | \code{height} parameters which provide a hint to the embedding environment 24 | about the ideal height/width for the app.} 25 | 26 | \item{enableBookmarking}{Can be one of \code{"url"}, \code{"server"}, or 27 | \code{"disable"}. The default value, \code{NULL}, will respect the setting from 28 | any previous calls to \code{\link[shiny:enableBookmarking]{enableBookmarking()}}. See \code{\link[shiny:enableBookmarking]{enableBookmarking()}} 29 | for more information on bookmarking your app.} 30 | 31 | \item{uiPattern}{A regular expression that will be applied to each \code{GET} 32 | request to determine whether the \code{ui} should be used to handle the 33 | request. Note that the entire request path must match the regular 34 | expression in order for the match to be considered successful.} 35 | 36 | \item{...}{arguments to pass to golem_opts. 37 | See `?golem::get_golem_options` for more details.} 38 | } 39 | \description{ 40 | Run the Shiny Application 41 | } 42 | -------------------------------------------------------------------------------- /dev/03_deploy.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ###################################### 11 | #### CURRENT FILE: DEPLOY SCRIPT ##### 12 | ###################################### 13 | 14 | # Test your app 15 | 16 | ## Run checks ---- 17 | ## Check the package before sending to prod 18 | devtools::check() 19 | rhub::check_for_cran() 20 | 21 | # Deploy 22 | 23 | ## Local, CRAN or Package Manager ---- 24 | ## This will build a tar.gz that can be installed locally, 25 | ## sent to CRAN, or to a package manager 26 | devtools::build() 27 | 28 | ## RStudio ---- 29 | ## If you want to deploy on RStudio related platforms 30 | golem::add_rstudioconnect_file() 31 | golem::add_shinyappsio_file() 32 | golem::add_shinyserver_file() 33 | 34 | ## Docker ---- 35 | ## If you want to deploy via a generic Dockerfile 36 | golem::add_dockerfile_with_renv() 37 | 38 | ## If you want to deploy to ShinyProxy 39 | golem::add_dockerfile_with_renv_shinyproxy() 40 | 41 | 42 | # Deploy to Posit Connect or ShinyApps.io 43 | # In command line. 44 | rsconnect::deployApp( 45 | appName = desc::desc_get_field("Package"), 46 | appTitle = desc::desc_get_field("Package"), 47 | appFiles = c( 48 | # Add any additional files unique to your app here. 49 | "R/", 50 | "inst/", 51 | "data/", 52 | "NAMESPACE", 53 | "DESCRIPTION", 54 | "app.R" 55 | ), 56 | appId = rsconnect::deployments(".")$appID, 57 | lint = FALSE, 58 | forceUpdate = TRUE 59 | ) 60 | -------------------------------------------------------------------------------- /srcjs/modules/plot.js: -------------------------------------------------------------------------------- 1 | 2 | import * as Plot from "@observablehq/plot"; 3 | 4 | function create_density_plot(element_id, data, x, y){ 5 | let ctx = document.getElementById(element_id); 6 | let existing_plots = ctx.querySelectorAll("div"); 7 | if(existing_plots.length > 0){ 8 | // Clear any existing plots before drawing. 9 | existing_plots.forEach((x) => { 10 | x.remove() 11 | }) 12 | }; 13 | var plt = Plot.plot({ 14 | color: { 15 | scheme: "ylgnbu" 16 | }, 17 | marks: [ 18 | Plot.hexagon( 19 | data, 20 | Plot.hexbin( 21 | { fill: "count" }, 22 | { x: x, y: y } 23 | ) 24 | ) 25 | ] 26 | }); 27 | var plot_el = document.createElement("div"); 28 | plot_el.appendChild(plt); 29 | ctx.appendChild(plot_el); 30 | }; 31 | 32 | const create_barchart = (el, data, x, y) => { 33 | let ctx = document.getElementById(el.id); 34 | let existing_plots = ctx.querySelectorAll("div"); 35 | if(existing_plots.length > 0){ 36 | // Clear any existing plots before drawing. 37 | existing_plots.forEach((x) => { 38 | x.remove() 39 | }) 40 | }; 41 | }; 42 | 43 | // update_plot seems like a good candidate for a closure. 44 | // We want to have a function that remembers `xvar` and `yvar`, 45 | // and only updates one of them depending on whichever is 46 | // changed. 47 | 48 | async function updatePlot(id){ 49 | let theElement = document.getElementById(id); 50 | let thePlot = theElement.querySelector('.plot-slot'); 51 | let xvar = null; 52 | let yvar = null; 53 | async function update(el, con){ 54 | // Figure out whether the element is an x-var or y-var selector. 55 | if(el.id.includes("x-var")){ 56 | xvar = el.value; 57 | } 58 | if(el.id.includes("y-var")){ 59 | yvar = el.value; 60 | } 61 | // Carry out the rest of the plot update logic. 62 | let data = await con.query(`SELECT ${xvar}, ${yvar} FROM payload`); 63 | create_density_plot(thePlot.id, data, xvar, yvar); 64 | } 65 | return update; 66 | }; 67 | 68 | export { create_density_plot, updatePlot }; 69 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const OptimizeWasmPlugin = require('optimize-wasm-webpack-plugin'); 4 | 5 | // defaults 6 | var outputPath = [], 7 | entryPoints = [], 8 | externals = [], 9 | misc = [], 10 | loaders = []; 11 | 12 | var outputPathFile = './srcjs/config/output_path.json', 13 | entryPointsFile = './srcjs/config/entry_points.json', 14 | externalsFile = './srcjs/config/externals.json', 15 | miscFile = './srcjs/config/misc.json', 16 | loadersFile = './srcjs/config/loaders.json'; 17 | 18 | // Read config files 19 | if(fs.existsSync(outputPathFile)){ 20 | outputPath = fs.readFileSync(outputPathFile, 'utf8'); 21 | } 22 | 23 | if(fs.existsSync(entryPointsFile)){ 24 | entryPoints = fs.readFileSync(entryPointsFile, 'utf8'); 25 | } 26 | 27 | if(fs.existsSync(externalsFile)){ 28 | externals = fs.readFileSync(externalsFile, 'utf8'); 29 | } 30 | 31 | if(fs.existsSync(miscFile)){ 32 | misc = fs.readFileSync(miscFile, 'utf8'); 33 | } 34 | 35 | if(fs.existsSync(loadersFile)){ 36 | loaders = fs.readFileSync(loadersFile, 'utf8'); 37 | } 38 | 39 | if(fs.existsSync(loadersFile)){ 40 | loaders = fs.readFileSync(loadersFile, 'utf8'); 41 | } 42 | 43 | // parse 44 | loaders = JSON.parse(loaders); 45 | misc = JSON.parse(misc); 46 | externals = JSON.parse(externals); 47 | entryPoints = JSON.parse(entryPoints); 48 | 49 | // parse regex 50 | loaders.forEach((loader) => { 51 | loader.test = RegExp(loader.test); 52 | return(loader); 53 | }) 54 | 55 | // placeholder for plugins 56 | var plugins = [ 57 | ]; 58 | 59 | // define options 60 | var options = { 61 | entry: entryPoints, 62 | output: { 63 | filename: '[name].js', 64 | path: path.resolve(__dirname, JSON.parse(outputPath)), 65 | }, 66 | externals: externals, 67 | module: { 68 | rules: loaders 69 | }, 70 | resolve: { 71 | extensions: ['.tsx', '.ts', '.js', '.wasm'], 72 | }, 73 | plugins: plugins, 74 | optimization: { 75 | minimizer: [new OptimizeWasmPlugin()] 76 | }, 77 | experiments: { 78 | futureDefaults: true 79 | } 80 | }; 81 | 82 | // add misc 83 | if(misc.resolve) 84 | options.resolve = misc.resolve; 85 | 86 | // export 87 | module.exports = options; 88 | -------------------------------------------------------------------------------- /srcjs/modules/database.js: -------------------------------------------------------------------------------- 1 | import * as duckdb from '@duckdb/duckdb-wasm'; 2 | 3 | // This is the function used when we're not loading from a CDN. 4 | // Remember that this generates two giant WASM files that Shiny is 5 | // responsible for serving each time someone connects. 6 | async function init_db(){ 7 | const MANUAL_BUNDLES = { 8 | mvp: { 9 | mainModule: duckdb_wasm, 10 | mainWorker: new URL('@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js', import.meta.url).toString(), 11 | }, 12 | eh: { 13 | mainModule: duckdb_wasm_next, 14 | mainWorker: new URL('@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js', import.meta.url).toString(), 15 | }, 16 | }; 17 | // Select a bundle based on browser checks 18 | const bundle = await duckdb.selectBundle(MANUAL_BUNDLES); 19 | // Instantiate the asynchronus version of DuckDB-wasm 20 | const worker = new Worker(bundle.mainWorker); 21 | const logger = new duckdb.ConsoleLogger(); 22 | const db = new duckdb.AsyncDuckDB(logger, worker); 23 | await db.instantiate(bundle.mainModule, bundle.pthreadWorker); 24 | return await db; 25 | }; 26 | 27 | async function initDBFromCDN(){ 28 | const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles(); 29 | const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES); 30 | const worker_url = URL.createObjectURL( 31 | new Blob([`importScripts("${bundle.mainWorker}");`], {type: 'text/javascript'}) 32 | ); 33 | // Instantiate the asynchronus version of DuckDB-WASM 34 | const worker = new Worker(worker_url); 35 | const logger = new duckdb.ConsoleLogger(); 36 | const db = new duckdb.AsyncDuckDB(logger, worker); 37 | await db.instantiate(bundle.mainModule, bundle.pthreadWorker); 38 | URL.revokeObjectURL(worker_url); 39 | return await db; 40 | }; 41 | 42 | async function create_table(con, data, name){ 43 | let data_ = new Uint8Array(data); 44 | try { 45 | await con.insertArrowFromIPCStream(data_, {name: name}); 46 | } catch (e) { 47 | console.log(e); 48 | } 49 | }; 50 | 51 | async function create_table2(con, data, name){ 52 | try { 53 | await con.insertArrowTable(data, {name: name}); 54 | } catch (e) { 55 | console.log(e); 56 | } 57 | }; 58 | 59 | async function delete_table(con, name){ 60 | let qry = `DROP TABLE IF EXISTS ${name};` 61 | try { 62 | await con.query(qry); 63 | } catch (e) { 64 | console.log(e); 65 | } 66 | }; 67 | 68 | export { init_db, initDBFromCDN, create_table, create_table2 }; 69 | 70 | 71 | -------------------------------------------------------------------------------- /dev/01_start.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ######################################## 11 | #### CURRENT FILE: ON START SCRIPT ##### 12 | ######################################## 13 | 14 | ## Fill the DESCRIPTION ---- 15 | ## Add meta data about your application 16 | ## 17 | ## /!\ Note: if you want to change the name of your app during development, 18 | ## either re-run this function, call golem::set_golem_name(), or don't forget 19 | ## to change the name in the app_sys() function in app_config.R /!\ 20 | ## 21 | golem::fill_desc( 22 | pkg_name = "app02", # The Name of the package containing the App 23 | pkg_title = "PKG_TITLE", # The Title of the package containing the App 24 | pkg_description = "PKG_DESC.", # The Description of the package containing the App 25 | author_first_name = "AUTHOR_FIRST", # Your First Name 26 | author_last_name = "AUTHOR_LAST", # Your Last Name 27 | author_email = "AUTHOR@MAIL.COM", # Your Email 28 | repo_url = NULL, # The URL of the GitHub Repo (optional), 29 | pkg_version = "0.0.0.9000" # The Version of the package containing the App 30 | ) 31 | 32 | ## Set {golem} options ---- 33 | golem::set_golem_options() 34 | 35 | ## Install the required dev dependencies ---- 36 | golem::install_dev_deps() 37 | 38 | ## Create Common Files ---- 39 | ## See ?usethis for more information 40 | usethis::use_mit_license("App02 User") # You can set another license here 41 | usethis::use_readme_rmd(open = FALSE) 42 | devtools::build_readme() 43 | # Note that `contact` is required since usethis version 2.1.5 44 | # If your {usethis} version is older, you can remove that param 45 | usethis::use_code_of_conduct(contact = "Golem User") 46 | usethis::use_lifecycle_badge("Experimental") 47 | usethis::use_news_md(open = FALSE) 48 | 49 | ## Use git ---- 50 | usethis::use_git() 51 | 52 | ## Init Testing Infrastructure ---- 53 | ## Create a template for tests 54 | golem::use_recommended_tests() 55 | 56 | ## Favicon ---- 57 | # If you want to change the favicon (default is golem's one) 58 | golem::use_favicon() # path = "path/to/ico". Can be an online file. 59 | # golem::remove_favicon() # Uncomment to remove the default favicon 60 | 61 | ## Add helper functions ---- 62 | golem::use_utils_ui(with_test = TRUE) 63 | golem::use_utils_server(with_test = TRUE) 64 | 65 | # You're now set! ---- 66 | 67 | # go to dev/02_dev.R 68 | rstudioapi::navigateToFile("dev/02_dev.R") 69 | -------------------------------------------------------------------------------- /R/app_ui.R: -------------------------------------------------------------------------------- 1 | #' The application User-Interface 2 | #' 3 | #' @param request Internal parameter for `{shiny}`. 4 | #' DO NOT REMOVE. 5 | #' @import shiny 6 | #' @noRd 7 | app_ui <- function(request) { 8 | tagList( 9 | # Leave this function for adding external resources 10 | golem_add_external_resources(), 11 | # Your application UI logic 12 | bslib::page_fluid( 13 | h1("Operations Dashboard"), 14 | hr(), 15 | fluidRow( 16 | inputPanel( 17 | selectInput(inputId = "company_filter", label = "Company", choices = letters), 18 | selectInput(inputId = "segment_filter", label = "Segment", choices = LETTERS), 19 | dateRangeInput(input = "date-range-filter", label = "Date Range") 20 | ), 21 | column(4), 22 | column( 23 | 4, 24 | actionButton(inputId = "send-arrow-btn", label = "SEND"), 25 | actionButton(inputId = "main-filter-btn", label = "Go") 26 | ), 27 | column(4) 28 | ), 29 | hr(), 30 | fluidRow( 31 | column( 32 | 6, 33 | tags$div( 34 | id = "plot_01_container", 35 | class = "plot-container", 36 | tags$div( 37 | id = "plot_01", 38 | class = "plot-slot" 39 | ), 40 | inputPanel( 41 | selectInput(inputId = "x-var-select-01", label = "X-Axis", choices = get_choices()), 42 | selectInput(inputId = "y-var-select-01", label = "Y-Axis", choices = get_choices()), 43 | ) 44 | ) 45 | ), 46 | column( 47 | 6, 48 | tags$div( 49 | id = "plot_02_container", 50 | class = "plot-container", 51 | tags$div( 52 | id = "plot_02", 53 | class = "plot-slot" 54 | ), 55 | inputPanel( 56 | selectInput(inputId = "x-var-select-02", label = "X-Axis", choices = get_choices()), 57 | selectInput(inputId = "y-var-select-02", label = "Y-Axis", choices = get_choices()), 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | ) 64 | } 65 | 66 | #' Add external Resources to the Application 67 | #' 68 | #' This function is internally used to add external 69 | #' resources inside the Shiny application. 70 | #' 71 | #' @import shiny 72 | #' @importFrom golem add_resource_path activate_js favicon bundle_resources 73 | #' @noRd 74 | golem_add_external_resources <- function() { 75 | add_resource_path( 76 | "www", 77 | app_sys("app/www") 78 | ) 79 | 80 | tags$head( 81 | favicon(), 82 | bundle_resources( 83 | path = app_sys("app/www"), 84 | app_title = "app02" 85 | ) 86 | # Add here other external resources 87 | # for example, you can add shinyalert::useShinyalert() 88 | ) 89 | } 90 | -------------------------------------------------------------------------------- /dev/02_dev.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ################################### 11 | #### CURRENT FILE: DEV SCRIPT ##### 12 | ################################### 13 | 14 | # Engineering 15 | 16 | ## Dependencies ---- 17 | ## Amend DESCRIPTION with dependencies read from package code parsing 18 | ## install.packages('attachment') # if needed. 19 | attachment::att_amend_desc() 20 | 21 | ## Add modules ---- 22 | ## Create a module infrastructure in R/ 23 | golem::add_module(name = "name_of_module1", with_test = TRUE) # Name of the module 24 | golem::add_module(name = "name_of_module2", with_test = TRUE) # Name of the module 25 | 26 | ## Add helper functions ---- 27 | ## Creates fct_* and utils_* 28 | golem::add_fct("helpers", with_test = TRUE) 29 | golem::add_utils("helpers", with_test = TRUE) 30 | 31 | ## External resources 32 | ## Creates .js and .css files at inst/app/www 33 | golem::add_js_file("script") 34 | golem::add_js_handler("handlers") 35 | golem::add_css_file("custom") 36 | golem::add_sass_file("custom") 37 | 38 | ## Add internal datasets ---- 39 | ## If you have data in your package 40 | usethis::use_data_raw(name = "my_dataset", open = FALSE) 41 | 42 | ## Tests ---- 43 | ## Add one line by test you want to create 44 | usethis::use_test("app") 45 | 46 | # Documentation 47 | 48 | ## Vignette ---- 49 | usethis::use_vignette("app02") 50 | devtools::build_vignettes() 51 | 52 | ## Code Coverage---- 53 | ## Set the code coverage service ("codecov" or "coveralls") 54 | usethis::use_coverage() 55 | 56 | # Create a summary readme for the testthat subdirectory 57 | covrpage::covrpage() 58 | 59 | ## CI ---- 60 | ## Use this part of the script if you need to set up a CI 61 | ## service for your application 62 | ## 63 | ## (You'll need GitHub there) 64 | usethis::use_github() 65 | 66 | # GitHub Actions 67 | usethis::use_github_action() 68 | # Chose one of the three 69 | # See https://usethis.r-lib.org/reference/use_github_action.html 70 | usethis::use_github_action_check_release() 71 | usethis::use_github_action_check_standard() 72 | usethis::use_github_action_check_full() 73 | # Add action for PR 74 | usethis::use_github_action_pr_commands() 75 | 76 | # Travis CI 77 | usethis::use_travis() 78 | usethis::use_travis_badge() 79 | 80 | # AppVeyor 81 | usethis::use_appveyor() 82 | usethis::use_appveyor_badge() 83 | 84 | # Circle CI 85 | usethis::use_circleci() 86 | usethis::use_circleci_badge() 87 | 88 | # Jenkins 89 | usethis::use_jenkins() 90 | 91 | # GitLab CI 92 | usethis::use_gitlab_ci() 93 | 94 | # You're now set! ---- 95 | # go to dev/03_deploy.R 96 | rstudioapi::navigateToFile("dev/03_deploy.R") 97 | --------------------------------------------------------------------------------