## quickgen_basic 的形参列表 default_funs <- c("n_obs", "mean", "sd", "min", "max") qgb_args <- as.list(formals(explore)) ## 收集用户输入的 reactive 列表 qgb_inputs <- reactive({ qgb_args$data_filter <- if (input$show_filter) input$data_filter else "" qgb_args$arr <- if (input$show_filter) input$data_arrange else "" qgb_args$rows <- if (input$show_filter) input$data_rows else "" qgb_args$dataset <- input$dataset for (i in r_drop(names(qgb_args))) { qgb_args[[i]] <- input[[paste0("qgb_", i)]] } qgb_args }) qgb_sum_args <- as.list(if (exists("summary.explore")) { formals(summary.explore) } else { formals(radiant.data:::summary.explore) }) ## list of function inputs selected by user qgb_sum_inputs <- reactive({ ## loop needed because reactive values don't allow single bracket indexing for (i in names(qgb_sum_args)) { qgb_sum_args[[i]] <- input[[paste0("qgb_", i)]] } qgb_sum_args }) ## UI-elements output$ui_qgb_vars <- renderUI({ vars <- varnames() req(available(vars)) selectInput( "qgb_vars", label = i18n$t("Numeric variable(s):"), choices = vars, selected = state_multiple("qgb_vars", vars, isolate(input$qgb_vars)), multiple = TRUE, size = min(8, length(vars)), selectize = FALSE ) }) output$ui_qgb_byvar <- renderUI({ withProgress(message = i18n$t("Acquiring variable information"), value = 1, { vars <- groupable_vars() }) req(available(vars)) if (any(vars %in% input$qgb_vars)) { vars <- base::setdiff(vars, input$qgb_vars) names(vars) <- varnames() %>% (function(x) x[match(vars, x)]) %>% names() } isolate({ ## if nothing is selected expl_byvar is also null if ("qgb_byvar" %in% names(input) && is.null(input$qgb_byvar)) { r_state$qgb_byvar <<- NULL } else { if (available(r_state$qgb_byvar) && all(r_state$qgb_byvar %in% vars)) { vars <- unique(c(r_state$qgb_byvar, vars)) names(vars) <- varnames() %>% (function(x) x[match(vars, x)]) %>% names() } } }) selectizeInput( "qgb_byvar", label = i18n$t("Group by:"), choices = vars, selected = state_multiple("qgb_byvar", vars, isolate(input$qgb_byvar)), multiple = TRUE, options = list( placeholder = i18n$t("Select group-by variable"), plugins = list("remove_button", "drag_drop") ) ) }) output$ui_qgb_fun <- renderUI({ r_funs <- getOption("radiant.functions") selected <- isolate( if (is.empty(input$qgb_fun)) default_funs else input$qgb_fun ) checkboxGroupInput( inputId = "qgb_fun", label = i18n$t("Apply function(s):"), choices = r_funs, selected = selected ) }) output$ui_qgb_top <- renderUI({ if (is.empty(input$qgb_vars)) { return() } top_var <- setNames( c("fun", "var", "byvar"), c(i18n$t("Function"), i18n$t("Variables"), i18n$t("Group by")) ) if (is.empty(input$qgb_byvar)) top_var <- top_var[1:2] selectizeInput( "qgb_top", label = i18n$t("Column header:"), choices = top_var, selected = state_single("qgb_top", top_var, isolate(input$qgb_top)), multiple = FALSE ) }) output$ui_qgb_name <- renderUI({ req(input$dataset) textInput("qgb_name", i18n$t("Store as:"), "", placeholder = i18n$t("Provide a table name")) }) output$ui_qgb_run <- renderUI({ ## updates when dataset changes req(input$dataset) actionButton("qgb_run", i18n$t("Create table"), width = "100%", icon = icon("play", verify_fa = FALSE), class = "btn-success") }) ## add a spinning refresh icon if the table needs to be (re)calculated run_refresh(qgb_args, "qgb", init = "vars", label = i18n$t("Create table"), relabel = i18n$t("Update table")) output$ui_quickgen_basic <- renderUI({ tagList( wellPanel( uiOutput("ui_qgb_run") ), wellPanel( uiOutput("ui_qgb_vars"), uiOutput("ui_qgb_byvar"), uiOutput("ui_qgb_fun"), uiOutput("ui_qgb_top"), # returnTextAreaInput("qgb_tab_slice", # label = i18n$t("Table slice (rows):"), # rows = 1, # value = state_init("qgb_tab_slice"), # placeholder = i18n$t("e.g., 1:5 and press return") # ), numericInput("qgb_dec", label = i18n$t("Decimals:"), value = state_init("qgb_dec", 3), min = 0) ), wellPanel( tags$table( tags$td(uiOutput("ui_qgb_name")), tags$td(actionButton("qgb_store", i18n$t("Store"), icon = icon("plus", verify_fa = FALSE)), class = "top") ) ), help_and_report( modal_title = i18n$t("Generate descriptive statistics with one click"), fun_name = "quickgen_basic", help_file = inclMD(file.path(getOption("radiant.path.quickgen"), "app/tools/help/quickgen_basic.md")), lic = "by-sa" ) ) }) .explore <- eventReactive(input$qgb_run, { if (not_available(input$qgb_vars) || is.null(input$qgb_top)) { return() } else if (!is.empty(input$qgb_byvar) && not_available(input$qgb_byvar)) { return() } else if (available(input$qgb_byvar) && any(input$qgb_byvar %in% input$qgb_vars)) { return() } qgbi <- qgb_inputs() qgbi$envir <- r_data sshhr(do.call(explore, qgbi)) }) observeEvent(input$qgb_search_columns, { r_state$qgb_search_columns <<- input$qgb_search_columns }) observeEvent(input$qgb_state, { r_state$qgb_state <<- input$qgb_state }) qgb_reset <- function(var, ncol) { if (!identical(r_state[[var]], input[[var]])) { r_state[[var]] <<- input[[var]] r_state$qgb_state <<- list() r_state$qgb_search_columns <<- rep("", ncol) } } output$explore <- DT::renderDataTable({ input$qgb_run withProgress(message = i18n$t("Generating explore table"), value = 1, { isolate({ qgb <- .explore() req(!is.null(qgb)) qgb$shiny <- TRUE ## resetting DT when changes occur nc <- ncol(qgb$tab) qgb_reset("qgb_vars", nc) qgb_reset("qgb_byvar", nc) qgb_reset("qgb_fun", nc) if (!is.null(r_state$qgb_top) && !is.null(input$qgb_top) && !identical(r_state$qgb_top, input$qgb_top)) { r_state$qgb_top <<- input$qgb_top r_state$qgb_state <<- list() r_state$qgb_search_columns <<- rep("", nc) } searchCols <- lapply(r_state$qgb_search_columns, function(x) list(search = x)) order <- r_state$qgb_state$order pageLength <- r_state$qgb_state$length }) caption <- if (is.empty(input$qgb_tab_slice)) NULL else glue("Table slice {input$expl_tab_slice} will be applied on Download, Store, or Report") dtab( qgb, dec = input$qgb_dec, searchCols = searchCols, order = order, pageLength = pageLength, caption = caption ) }) }) dl_qgb_tab <- function(path) { dat <- try(.explore(), silent = TRUE) if (inherits(dat, "try-error") || is.null(dat)) { write.csv(tibble::tibble("Data" = "[Empty]"), path, row.names = FALSE) } else { rows <- input$qgb_rows_all dat$tab %>% (function(x) if (is.null(rows)) x else x[rows, , drop = FALSE]) %>% #(function(x) if (is.empty(input$qgb_tab_slice)) x else slice_data(x, input$qgb_tab_slice)) %>% write.csv(path, row.names = FALSE) } } download_handler( id = "dl_qgb_tab", fun = dl_qgb_tab, fn = function() paste0(input$dataset, "_qgb"), type = "csv" ) observeEvent(input$qgb_store, { req(input$qgb_name) dat <- .explore() if (is.null(dat)) { return() } dataset <- fix_names(input$qgb_name) if (input$qgb_name != dataset) { updateTextInput(session, inputId = "qgb_name", value = dataset) } rows <- input$qgb_rows_all dat$tab <- dat$tab %>% (function(x) if (is.null(rows)) x else x[rows, , drop = FALSE]) %>% #(function(x) if (is.empty(input$qgb_tab_slice)) x else slice_data(x, input$qgb_tab_slice)) r_data[[dataset]] <- dat$tab register(dataset) updateSelectInput(session, "dataset", selected = input$dataset) showModal( modalDialog( title = i18n$t("Data Stored"), span( i18n$t("Dataset was successfully added to the datasets dropdown. Add code to Report > Rmd or Report > R to (re)create the results by clicking the report icon on the bottom left of your screen.") ), footer = modalButton(i18n$t("OK")), size = "m", easyClose = TRUE ) ) }) qgb_report <- function() { ## get the state of the dt table ts <- dt_state("explore") xcmd <- "# summary(result)\ndtab(result" if (!is.empty(input$qgb_dec, 3)) { xcmd <- paste0(xcmd, ", dec = ", input$qgb_dec) } if (!is.empty(r_state$qgb_state$length, 10)) { xcmd <- paste0(xcmd, ", pageLength = ", r_state$qgb_state$length) } xcmd <- paste0(xcmd, ", caption = \"\") %>% render()") if (!is.empty(input$qgb_name)) { dataset <- fix_names(input$qgb_name) if (input$qgb_name != dataset) { updateTextInput(session, inputId = "qgb_name", value = dataset) } xcmd <- paste0(xcmd, "\n", dataset, " <- result$tab\nregister(\"", dataset, "\")") } inp_main <- clean_args(qgb_inputs(), qgb_args) if (ts$tabsort != "") inp_main <- c(inp_main, tabsort = ts$tabsort) if (ts$tabfilt != "") inp_main <- c(inp_main, tabfilt = ts$tabfilt) if (is.empty(inp_main$rows)) { inp_main$rows <- NULL } if (is.empty(input$qgb_tab_slice)) { inp_main <- c(inp_main, nr = Inf) } else { inp_main$tabslice <- input$qgb_tab_slice } inp_out <- list(clean_args(qgb_sum_inputs(), qgb_sum_args[-1])) update_report( inp_main = inp_main, fun_name = "qgb", inp_out = inp_out, outputs = c(), figs = FALSE, xcmd = xcmd ) } observeEvent(input$qgb_report, { r_info[["latest_screenshot"]] <- NULL qgb_report() }) observeEvent(input$qgb_screenshot, { r_info[["latest_screenshot"]] <- NULL radiant_screenshot_modal("modal_qgb_screenshot") }) observeEvent(input$modal_qgb_screenshot, { qgb_report() removeModal() }) output$quickgen_basic <- renderUI({ stat_tab_panel( menu = i18n$t("Oneclick generation > Generate descriptive statistics"), tool = i18n$t("Generate descriptive statistics with one click"), tool_ui = "ui_quickgen_basic", output_panels = tagList( tabPanel( title = i18n$t("Table"), download_link("dl_qgb_tab"),br(), DT::dataTableOutput("explore") ) ) ) })