diff --git a/R/sysdata.rda b/R/sysdata.rda index c01e454e..a208d0b2 100644 Binary files a/R/sysdata.rda and b/R/sysdata.rda differ diff --git a/R/validation.R b/R/validation.R index 7929f65a..3367163c 100644 --- a/R/validation.R +++ b/R/validation.R @@ -317,7 +317,7 @@ validation_lib <- function(name = NULL) { "missings" = function(x) { ### Placeholder for missingness validation list( - string = i18n$t("There are {p_miss} % missing observations."), + string = i18n$t("There is a total of {p_miss} % missing observations."), summary.fun = missings_validate, summary.fun.args = list( data = x diff --git a/SESSION.md b/SESSION.md index 51e20b56..20c1df1c 100644 --- a/SESSION.md +++ b/SESSION.md @@ -43,6 +43,7 @@ |cardx |0.2.5 |2025-07-03 |CRAN (R 4.4.1) | |caTools |1.18.3 |2024-09-04 |CRAN (R 4.4.1) | |cellranger |1.1.0 |2016-07-27 |CRAN (R 4.4.0) | +|cffr |1.2.0 |2025-01-25 |CRAN (R 4.4.1) | |checkmate |2.3.2 |2024-07-29 |CRAN (R 4.4.0) | |class |7.3-23 |2025-01-01 |CRAN (R 4.4.1) | |classInt |0.4-11 |2025-01-08 |CRAN (R 4.4.1) | @@ -52,6 +53,7 @@ |colorspace |2.1-1 |2024-07-26 |CRAN (R 4.4.1) | |commonmark |2.0.0 |2025-07-07 |CRAN (R 4.4.1) | |crayon |1.5.3 |2024-06-20 |CRAN (R 4.4.1) | +|curl |6.4.0 |2025-06-22 |CRAN (R 4.4.1) | |data.table |1.17.8 |2025-07-10 |CRAN (R 4.4.1) | |datamods |1.5.3 |2024-10-02 |CRAN (R 4.4.1) | |datawizard |1.2.0 |2025-07-17 |CRAN (R 4.4.1) | @@ -110,6 +112,7 @@ |iterators |1.0.14 |2022-02-05 |CRAN (R 4.4.1) | |jquerylib |0.1.4 |2021-04-26 |CRAN (R 4.4.0) | |jsonlite |2.0.0 |2025-03-27 |CRAN (R 4.4.1) | +|jsonvalidate |1.5.0 |2025-02-07 |CRAN (R 4.4.1) | |KernSmooth |2.23-26 |2025-01-01 |CRAN (R 4.4.1) | |keyring |1.4.1 |2025-06-15 |CRAN (R 4.4.1) | |knitr |1.50 |2025-03-16 |CRAN (R 4.4.1) | @@ -219,6 +222,7 @@ |usethis |3.1.0 |2024-11-26 |CRAN (R 4.4.1) | |utf8 |1.2.6 |2025-06-08 |CRAN (R 4.4.1) | |uuid |1.2-1 |2024-07-29 |CRAN (R 4.4.1) | +|V8 |6.0.6 |2025-08-18 |CRAN (R 4.4.1) | |vctrs |0.6.5 |2023-12-01 |CRAN (R 4.4.0) | |viridis |0.6.5 |2024-01-29 |CRAN (R 4.4.0) | |viridisLite |0.4.2 |2023-05-02 |CRAN (R 4.4.1) | diff --git a/app_docker/www/umami-app.html b/app_docker/www/umami-app.html index 05820534..780f6478 100644 --- a/app_docker/www/umami-app.html +++ b/app_docker/www/umami-app.html @@ -1 +1 @@ - + diff --git a/inst/apps/FreesearchR/app.R b/inst/apps/FreesearchR/app.R index 1873202f..407dca07 100644 --- a/inst/apps/FreesearchR/app.R +++ b/inst/apps/FreesearchR/app.R @@ -1,10 +1,10 @@ ######## -#### Current file: /var/folders/9l/xbc19wxx0g79jdd2sf_0v291mhwh7f/T//Rtmp4BY9Rb/file17e654c25f197.R +#### Current file: /var/folders/9l/xbc19wxx0g79jdd2sf_0v291mhwh7f/T//RtmphmfdDq/filea00a3587a79a.R ######## -trans_path <- system.file("translations", package = "FreesearchR") +i18n_path <- system.file("translations", package = "FreesearchR") ######## @@ -47,7 +47,7 @@ library(rlang) library(shiny.i18n) ## Translation init -i18n <- shiny.i18n::Translator$new(translation_csvs_path = trans_path) +i18n <- shiny.i18n::Translator$new(translation_csvs_path = i18n_path) i18n$set_translation_language("en") @@ -62,7 +62,7 @@ i18n$set_translation_language("en") #### Current file: /Users/au301842/FreesearchR/R//app_version.R ######## -app_version <- function()'25.8.3' +app_version <- function()'25.9.1' ######## @@ -254,7 +254,8 @@ data_correlations_server <- function(id, # ns <- session$ns rv <- shiny::reactiveValues( - data = NULL + data = NULL, + pairs = NULL ) rv$data <- shiny::reactive({ @@ -266,17 +267,30 @@ data_correlations_server <- function(id, out <- data() } # out |> dplyr::mutate(dplyr::across(tidyselect::everything(),as.numeric)) - sapply(out,as.numeric) + sapply(out, as.numeric) # as.numeric() }) # rv <- list() # rv$data <- mtcars + rv$pairs <- shiny::reactive({ + shiny::req(rv$data) + shiny::req(cutoff) + # tryCatch( + # { + correlation_pairs(rv$data(), threshold = cutoff()) + # }, + # error = function(err) { + # showNotification(paste0(i18n$t("The following error occured on determining correlations: "), err), type = "err") + # } + # ) + }) + output$suggest <- shiny::renderPrint({ shiny::req(rv$data) shiny::req(cutoff) - pairs <- correlation_pairs(rv$data(), threshold = cutoff()) + pairs <- rv$pairs() more <- ifelse(nrow(pairs) > 1, i18n$t("from each pair"), "") @@ -315,19 +329,40 @@ data_correlations_server <- function(id, ) # psych::pairs.panels(rv$data()) }) + + return(shiny::isolate(rv$pairs)) } ) } + +#' Determine significant correlations in the data set +#' +#' @param data data.frame +#' @param threshold correlation threshold +#' +#' @returns data.frame +#' @export +#' +#' @examples +#' correlation_pairs(mtcars) +#' correlation_pairs(mtcars,.9) +#' correlation_pairs(mtcars[c(1:4),]) correlation_pairs <- function(data, threshold = .8) { data <- as.data.frame(data)[!sapply(as.data.frame(data), is.character)] - data <- sapply(data,\(.x)if (is.factor(.x)) as.numeric(.x) else .x) |> as.data.frame() + data <- sapply(data, \(.x)if (is.factor(.x)) as.numeric(.x) else .x) |> as.data.frame() # data <- data |> dplyr::mutate(dplyr::across(dplyr::where(is.factor), as.numeric)) - cor <- Hmisc::rcorr(as.matrix(data)) - r <- cor$r %>% as.table() - d <- r |> - as.data.frame() |> - dplyr::filter(abs(Freq) > threshold, Freq != 1) + if (nrow(data) > 4) { + cor <- Hmisc::rcorr(as.matrix(data)) + r <- cor$r %>% as.table() + d <- r |> + as.data.frame() |> + dplyr::filter(abs(Freq) > threshold, Freq != 1) + } else { + expand.grid(names(data),names(data)) + d <- data.frame(matrix(ncol = 3)) + d <- d[!is.na(d[3]),] + } d[1:2] |> apply(1, \(.x){ @@ -354,10 +389,6 @@ sentence_paste <- function(data, and.str = "and") { } - - - - ######## #### Current file: /Users/au301842/FreesearchR/R//create-column-mod.R ######## @@ -1635,9 +1666,11 @@ data_visuals_ui <- function(id, tab_title = "Plots", ...) { bslib::layout_sidebar( sidebar = bslib::sidebar( bslib::accordion( + id = "acc_plot", multiple = FALSE, bslib::accordion_panel( - title = "Creating plot", + value = "acc_pan_plot", + title = "Create plot", icon = bsicons::bs_icon("graph-up"), shiny::uiOutput(outputId = ns("primary")), shiny::helpText(i18n$t('Only non-text variables are available for plotting. Go the "Data" to reclass data to plot.')), @@ -1656,6 +1689,7 @@ data_visuals_ui <- function(id, tab_title = "Plots", ...) { shiny::helpText(i18n$t('Adjust settings, then press "Plot".')) ), bslib::accordion_panel( + value = "acc_pan_download", title = "Download", icon = bsicons::bs_icon("download"), shinyWidgets::noUiSliderInput( @@ -1745,6 +1779,11 @@ data_visuals_server <- function(id, code = NULL ) + shiny::observe({ + bslib::accordion_panel_update(id = "acc_plot", target = "acc_pan_plot",title = i18n$t("Create plot")) + bslib::accordion_panel_update(id = "acc_plot", target = "acc_pan_download",title = i18n$t("Download")) + }) + # ## --- New attempt # # rv$plot.params <- shiny::reactive({ @@ -2027,7 +2066,7 @@ data_visuals_server <- function(id, plot <- rv$plot[[1]] } # browser() - shiny::withProgress(message = i18n$t("Drawing the plot. Hold on for a moment.."), { + shiny::withProgress(message = i18n$t("Drawing the plot. Hold tight for a moment.."), { ggplot2::ggsave( filename = file, plot = plot, @@ -4068,7 +4107,7 @@ simple_snake <- function(data){ #### Current file: /Users/au301842/FreesearchR/R//hosted_version.R ######## -hosted_version <- function()'v25.8.3-250922' +hosted_version <- function()'v25.9.1-250923' ######## @@ -4801,12 +4840,12 @@ data_missings_server <- function(id, if (is.null(variabler()) || variabler() == "" || !variabler() %in% names(datar())) { if (anyNA(datar())){ - title <- "No variable chosen for analysis" + title <- i18n$t("No variable chosen for analysis") } else { - title <- "No missing observations" + title <- i18n$t("No missing observations") } } else { - title <- glue::glue("Missing vs non-missing observations in the variable **'{variabler()}'**") + title <- glue::glue(i18n$t("Missing vs non-missing observations in the variable **'{variabler()}'**")) } out <- rv$data() |> @@ -7481,10 +7520,11 @@ regression_ui <- function(id, ...) { sidebar = bslib::sidebar( shiny::uiOutput(outputId = ns("data_info"), inline = TRUE), bslib::accordion( + id = "acc_reg", open = "acc_reg", multiple = FALSE, bslib::accordion_panel( - value = "acc_reg", + value = "acc_pan_reg", title = "Regression", icon = bsicons::bs_icon("calculator"), shiny::uiOutput(outputId = ns("outcome_var")), @@ -7563,14 +7603,15 @@ regression_ui <- function(id, ...) { bslib::layout_sidebar( sidebar = bslib::sidebar( bslib::accordion( - open = "acc_reg", + id = "acc_coef_plot", + open = "acc_pan_coef_plot", multiple = FALSE, do.call( bslib::accordion_panel, c( list( - value = "acc_plot", - title = "Coefficient plot", + value = "acc_pan_coef_plot", + title = "Coefficients plot", icon = bsicons::bs_icon("bar-chart-steps"), shiny::tags$br(), shiny::uiOutput(outputId = ns("plot_model")) @@ -7629,10 +7670,11 @@ regression_ui <- function(id, ...) { bslib::layout_sidebar( sidebar = bslib::sidebar( bslib::accordion( - open = "acc_reg", + id = "acc_checks", + open = "acc_pan_checks", multiple = FALSE, bslib::accordion_panel( - value = "acc_checks", + value = "acc_pan_checks", title = "Checks", icon = bsicons::bs_icon("clipboard-check"), shiny::uiOutput(outputId = ns("plot_checks")) @@ -7669,6 +7711,12 @@ regression_server <- function(id, } }) + shiny::observe({ + bslib::accordion_panel_update(id = "acc_reg", target = "acc_pan_reg", title = i18n$t("Regression")) + bslib::accordion_panel_update(id = "acc_coef_plot", target = "acc_pan_coef_plot", title = i18n$t("Coefficients plot")) + bslib::accordion_panel_update(id = "acc_checks", target = "acc_pan_checks", title = i18n$t("Checks")) + }) + output$data_info <- shiny::renderUI({ shiny::req(regression_vars()) shiny::req(data_r()) @@ -8550,7 +8598,7 @@ ui_elements <- function(selection) { width = 9, shiny::uiOutput(outputId = "data_info", inline = TRUE), shiny::tags$p( - i18n$t("Below you find a summary table for quick insigths, and on the right you can visualise data classes, browse data and apply different data filters.") + i18n$t("Below you find a summary table for quick insigths, and on the right you can visualise data classes, browse observations and apply different data filters.") ) ), shiny::column( @@ -8565,7 +8613,7 @@ ui_elements <- function(selection) { shiny::br(), shiny::actionButton( inputId = "modal_browse", - label = i18n$t("Browse data"), + label = i18n$t("Browse observations"), width = "100%", disabled = TRUE ), @@ -8720,11 +8768,12 @@ ui_elements <- function(selection) { sidebar = bslib::sidebar( shiny::uiOutput(outputId = "data_info_nochar", inline = TRUE), bslib::accordion( + id="acc_chars", open = "acc_chars", multiple = FALSE, bslib::accordion_panel( open = TRUE, - value = "acc_chars", + value = "acc_pan_chars", title = "Settings", icon = bsicons::bs_icon("table"), shiny::uiOutput("strat_var"), @@ -8765,11 +8814,12 @@ ui_elements <- function(selection) { sidebar = bslib::sidebar( # shiny::uiOutput(outputId = "data_info_nochar", inline = TRUE), bslib::accordion( + id="acc_cor", open = "acc_chars", multiple = FALSE, bslib::accordion_panel( - value = "acc_cor", - title = "Correlations", + value = "acc_pan_cor", + title = "Settings", icon = bsicons::bs_icon("bounding-box"), shiny::uiOutput("outcome_var_cor"), shiny::helpText("To avoid evaluating the correlation of the outcome variable, this can be excluded from the plot or select 'none'."), @@ -8797,17 +8847,19 @@ ui_elements <- function(selection) { bslib::layout_sidebar( sidebar = bslib::sidebar( bslib::accordion( + id = "acc_mis", open = "acc_chars", multiple = FALSE, bslib::accordion_panel( - vlaue = "acc_mis", - title = "Missings", + value = "acc_pan_mis", + title = "Settings", icon = bsicons::bs_icon("x-circle"), shiny::uiOutput("missings_var"), shiny::helpText("To consider if data is missing by random, choose the outcome/dependent variable, if it has any missings to evaluate if there is a significant difference across other variables depending on missing data or not.") ) ) ), + validation_ui("validation_mcar"), data_missings_ui(id = "missingness") ) ) @@ -10200,13 +10252,17 @@ validation_server <- function(id, { # browser() to_validate <- data_r() - if (is.reactivevalues(to_validate)) + if (is.reactivevalues(to_validate)) { + to_validate <- reactiveValuesToList(to_validate) + } + if (!is.data.frame(to_validate)) { + # browser() out <- lapply( - reactiveValuesToList(to_validate), - make_validation_alerts) |> - purrr::list_flatten() - - if (length(to_validate) > 0) { + to_validate, + make_validation_alerts + ) |> + purrr::list_flatten() + } else if (length(to_validate) > 0) { out <- make_validation_alerts(to_validate) } valid_ui$x <- tagList(out) @@ -10296,12 +10352,12 @@ obs_filter_validate <- function(before, after) { #' #' @examples #' df <- mtcars -#' df[1,2:4] <- NA +#' df[1, 2:4] <- NA #' missings_validate(df) -missings_validate <- function(data){ +missings_validate <- function(data) { if (!0 %in% dim(data)) { # browser() - p_miss <- sum(is.na(data))/prod(dim(data))*100 + p_miss <- sum(is.na(data)) / prod(dim(data)) * 100 data.frame( p_miss = p_miss ) |> @@ -10318,6 +10374,52 @@ missings_validate <- function(data){ } } +#' Correlation pairs validation +#' +#' @param data data.frame +#' +#' @returns data.frame +#' @export +#' +#' @examples +#' # correlation_pairs(mtcars) |> corr_pairs_validate() +corr_pairs_validate <- function(data) { + data_s <- if (shiny::is.reactive(data)) data() else data + if (!0 %in% dim(data_s)) { + # browser() + n_pairs <- nrow(data_s) + data.frame( + n_pairs = n_pairs + ) + } else { + data.frame(NULL) + } +} + +#' MCAR validation based on a gtsummary table bady +#' +#' @param data data +#' @param outcome outcome variable +#' +#' @returns data.frame +#' @export +#' +mcar_validate <- function(data, outcome=NULL) { + data_s <- if (shiny::is.reactive(data)) data() else data + + if (is.data.frame(data_s) && "p.value" %in% names(data_s) && !is.null(outcome)) { + # browser() + n_nonmcar <- sum(data_s["p.value"][!is.na(data_s["p.value"])] < 0.05) + + data.frame( + n_nonmcar = n_nonmcar, + outcome = outcome + ) + } else { + data.frame(NULL) + } +} + ######################################################################## ############# Collected validation functions in a library-like function @@ -10333,7 +10435,7 @@ missings_validate <- function(data){ #' @examples #' validation_lib() #' validation_lib("missings") -validation_lib <- function(name=NULL) { +validation_lib <- function(name = NULL) { ls <- list( "obs_filter" = function(x, y) { ## Validation function for observations filter @@ -10367,10 +10469,10 @@ validation_lib <- function(name=NULL) { test.fun.args = list(var = "p_out", cut = 50) ) }, - "missings" = function(x, y) { + "missings" = function(x) { ### Placeholder for missingness validation list( - string = "There are {p_miss} % missing observations.", + string = i18n$t("There is a total of {p_miss} % missing observations."), summary.fun = missings_validate, summary.fun.args = list( data = x @@ -10381,11 +10483,42 @@ validation_lib <- function(name=NULL) { }, test.fun.args = list(var = "p_miss", cut = 30) ) + }, + "mcar" = function(x, y) { + ### Placeholder for missingness validation + list( + string = i18n$t("There is a significant correlation between {n_nonmcar} variables and missing observations in the outcome variable {outcome}."), + summary.fun = mcar_validate, + summary.fun.args = list( + data = x, + outcome = y + ), + test.fun = function(x, var, cut) { + test.var <- x[var] + ifelse(test.var > cut, "warning", "succes") + }, + test.fun.args = list(var = "n_nonmcar", cut = 0) + ) + }, + "corr_pairs" = function(x) { + ### Placeholder for missingness validation + list( + string = i18n$t("Data includes {n_pairs} pairs of highly correlated variables."), + summary.fun = corr_pairs_validate, + summary.fun.args = list( + data = x + ), + test.fun = function(x, var, cut) { + test.var <- x[var] + ifelse(test.var > cut, "warning", "succes") + }, + test.fun.args = list(var = "n_pairs", cut = 0) + ) } ) - if (!is.null(name)){ - name <- match.arg(name,choices = names(ls)) + if (!is.null(name)) { + name <- match.arg(name, choices = names(ls)) ls[[name]] } else { ls @@ -10409,15 +10542,16 @@ validation_lib <- function(name=NULL) { #' i18n <- shiny.i18n::Translator$new(translation_csvs_path = here::here("inst/translations")) #' i18n$set_translation_language("en") #' df_original <- mtcars -#' df_original[1,2:4] <- NA -#' df_obs <- df_original |> dplyr::filter(carb==4) +#' df_original[1, 2:4] <- NA +#' df_obs <- df_original |> dplyr::filter(carb == 4) #' df_vars <- df_original[1:7] #' val <- purrr::map2( #' .x = validation_lib(), #' .y = list( -#' list(x = df_original, y = df_obs), -#' list(x = df_original, y = df_vars), -#' list(x=df_original)), +#' list(x = df_original, y = df_obs), +#' list(x = df_original, y = df_vars), +#' list(x = df_original) +#' ), #' make_validation #' ) #' val |> make_validation_alerts() @@ -10463,7 +10597,7 @@ make_validation <- function(ls, ...) { #' @export make_validation_alerts <- function(data) { # browser() - if (is.data.frame(data)){ + if (is.data.frame(data)) { ls <- list(data) } else { ls <- data @@ -11025,18 +11159,7 @@ ui <- bslib::page_fixed( # ui_elements$feedback, # ui_elements$docs, fillable = FALSE, - footer = shiny::tags$footer( - style = "background-color: #14131326; padding: 4px; text-align: center; bottom: 0; width: 100%;", - shiny::uiOutput(outputId = "language_select"), - shiny::p( - style = "margin: 1", - "Data is only stored for analyses and deleted when the app is closed.", shiny::markdown("Consider [running ***FreesearchR*** locally](https://agdamsbo.github.io/FreesearchR/#run-locally-on-your-own-machine) if working with sensitive data.") - ), - shiny::p( - style = "margin: 1; color: #888;", - shiny::tags$a("Documentation", href = "https://agdamsbo.github.io/FreesearchR/", target = "_blank", rel = "noopener noreferrer"), " | ", hosted_version(), " | ", shiny::tags$a("License: AGPLv3", href = "https://github.com/agdamsbo/FreesearchR/blob/main/LICENSE.md", target = "_blank", rel = "noopener noreferrer"), " | ", shiny::tags$a("Source", href = "https://github.com/agdamsbo/FreesearchR/", target = "_blank", rel = "noopener noreferrer"), " | ", shiny::tags$a("Share feedback", href = "https://redcap.au.dk/surveys/?s=JPCLPTXYDKFA8DA8", target = "_blank", rel = "noopener noreferrer") - ), - ) + footer = shiny::uiOutput("footer_text_div") ) ) ) @@ -11112,7 +11235,8 @@ server <- function(input, output, session) { data_variables = NULL, data_filtered = NULL, models = NULL, - code = list() + code = list(), + corr_pairs = NULL ) ############################################################################## @@ -11123,7 +11247,10 @@ server <- function(input, output, session) { rv_validations <- shiny::reactiveValues( obs_filter = NULL, - vars_filter = NULL, + var_filter = NULL, + # missings = NULL, + corr_pairs = NULL, + mcar = NULL, validations = NULL ) @@ -11144,7 +11271,9 @@ server <- function(input, output, session) { inputId = "language_select", label = "", selected = "en", - choices = language_choices() + choices = language_choices(), + # selectize = TRUE, + width = "140px" ) }) @@ -11197,10 +11326,45 @@ server <- function(input, output, session) { output$intro_text <- renderUI(includeHTML(i18n$t("www/intro.html"))) }) + shiny::observe( + output$footer_text_div <- renderUI({ + shiny::tags$footer( + style = "background-color: #14131326; padding: 4px; text-align: center; bottom: 0; width: 100%;", + shiny::p( + style = "margin: 1", + i18n$t("Data is only stored for analyses and deleted when the app is closed."), shiny::markdown(i18n$t("Consider [running ***FreesearchR*** locally](https://agdamsbo.github.io/FreesearchR/#run-locally-on-your-own-machine) if working with sensitive data.")) + ), + # shiny::p( + # style = "margin: 1; color: #888;", + div( + style = "display: inline-flex; align-items: center; gap: 1px;", + shiny::tags$a(i18n$t("Documentation"), href = "https://agdamsbo.github.io/FreesearchR/", target = "_blank", rel = "noopener noreferrer"), " | ", div( + style = "display: inline-block;", + class = c("smart-dropdown", "text-select"), + shiny::uiOutput(outputId = "language_select") + ), " | ", shiny::tags$a(i18n$t("Feedback"), href = "https://redcap.au.dk/surveys/?s=JPCLPTXYDKFA8DA8", target = "_blank", rel = "noopener noreferrer") + ), + br(), + p( + style = "display: inline-flex; align-items: center; gap: 1px;", + hosted_version(), " | ", shiny::tags$a(i18n$t("License: AGPLv3"), href = "https://github.com/agdamsbo/FreesearchR/blob/main/LICENSE.md", target = "_blank", rel = "noopener noreferrer"), " | ", shiny::tags$a(i18n$t("Source"), href = "https://github.com/agdamsbo/FreesearchR/", target = "_blank", rel = "noopener noreferrer"), + ) + ) + }) + ) + + shiny::observeEvent(input$language_select, { + bslib::accordion_panel_update(id = "acc_chars", title = i18n$t("Settings"), target = "acc_pan_chars") + bslib::accordion_panel_update(id = "acc_cor", title = i18n$t("Settings"), target = "acc_pan_cor") + bslib::accordion_panel_update(id = "acc_mis", title = i18n$t("Settings"), target = "acc_pan_mis") + }) + + output$redcap_warning <- shiny::renderUI({ rv_alerts$redcap_alert }) + ############################################################################## ######### ######### Data import section @@ -11526,15 +11690,16 @@ server <- function(input, output, session) { # rv$code$modify[[length(rv$code$modify) + 1]] <- attr(rv$data, "code") }) - shiny::observeEvent( + shiny::observe( ## This could possibly be rewritten to include all validations ## and rendering would just subset relevant or all - list( - rv$data, - rv$data_filtered, - rv$data_variables, - input$language_select - ), + # list( + # rv$data, + # rv$data_filtered, + # rv$data_variables, + # rv$corr_pairs, + # input$language_select + # ), { if (!is.null(rv$data_filtered)) { rv_validations$obs_filter <- make_validation( @@ -11564,6 +11729,29 @@ server <- function(input, output, session) { ) ) } + + if (!is.null(rv$corr_pairs())) { + req(rv$corr_pairs()) + rv_validations$corr_pairs <- make_validation( + ls = validation_lib("corr_pairs"), + list( + x = rv$corr_pairs + ) + ) + } + + # mcar_validate(data=rv$missings()[["_data"]],outcome = input$missings_var) + if (!is.null(rv$missings())) { + req(rv$missings()) + req(input$missings_var) + rv_validations$mcar <- make_validation( + ls = validation_lib("mcar"), + list( + x = rv$missings()[["_data"]], + y = input$missings_var + ) + ) + } } ) @@ -11591,11 +11779,23 @@ server <- function(input, output, session) { } ) + shiny::observeEvent( + rv_validations$mcar, + { + validation_server( + id = "validation_mcar", + data = rv_validations$mcar + ) + } + ) + shiny::observeEvent( list( - rv_validations$var_filter, - rv_validations$obs_filter, - rv_validations$missings + rv_validations$var_filter, + rv_validations$obs_filter, + rv_validations$missings, + rv_validations$mcar, + rv_validations$corr_pairs ), { validation_server( @@ -11881,7 +12081,7 @@ server <- function(input, output, session) { ) }) - data_correlations_server( + rv$corr_pairs <- data_correlations_server( id = "correlations", data = shiny::reactive({ shiny::req(rv$list$data) @@ -11894,16 +12094,18 @@ server <- function(input, output, session) { cutoff = shiny::reactive(input$cor_cutoff) ) - output$missings_var <- shiny::renderUI({ - columnSelectInput( - inputId = "missings_var", - label = "Select variable to stratify analysis", - data = shiny::reactive({ - shiny::req(rv$data_filtered) - rv$data_filtered[apply(rv$data_filtered, 2, anyNA)] - })() - ) - }) + shiny::observe( + output$missings_var <- shiny::renderUI({ + columnSelectInput( + inputId = "missings_var", + label = "Select variable to stratify analysis", + data = shiny::reactive({ + shiny::req(rv$data_filtered) + rv$data_filtered[apply(rv$data_filtered, 2, anyNA)] + })() + ) + }) + ) rv$missings <- data_missings_server( id = "missingness", @@ -11911,6 +12113,14 @@ server <- function(input, output, session) { variable = shiny::reactive(input$missings_var) ) + # shiny::observe({ + # req(rv$missings()) + # browser() + # # table <- rv$missings() + # + # mcar_validate(data=rv$missings()[["_data"]],outcome = input$missings_var) + # }) + ############################################################################## ######### @@ -11999,7 +12209,7 @@ server <- function(input, output, session) { ) }, error = function(err) { - showNotification(paste0("We encountered the following error creating your report: ", err), type = "err") + showNotification(paste0(i18n$t("We encountered the following error creating your report: "), err), type = "err") } ) }) diff --git a/inst/apps/FreesearchR/www/scripts.js b/inst/apps/FreesearchR/www/scripts.js index 67209470..098216dd 100644 --- a/inst/apps/FreesearchR/www/scripts.js +++ b/inst/apps/FreesearchR/www/scripts.js @@ -72,3 +72,22 @@ $(document).on('shiny:sessioninitialized', function() { }); +// Flip-down flip-up + +$(document).on('focus', '.smart-dropdown .selectize-control input', function() { + var $dropdown = $(this).closest('.selectize-control').find('.selectize-dropdown'); + var $container = $(this).closest('.smart-dropdown'); + + var containerBottom = $container.offset().top + $container.outerHeight(); + var windowHeight = $(window).height(); + var scrollTop = $(window).scrollTop(); + var viewportBottom = scrollTop + windowHeight; + + // If there's not enough space below, flip up + if (containerBottom + 200 > viewportBottom) { + $container.addClass('flip-up'); + } else { + $container.removeClass('flip-up'); + } + }); + diff --git a/inst/apps/FreesearchR/www/style.css b/inst/apps/FreesearchR/www/style.css index ce38e394..64336dd1 100644 --- a/inst/apps/FreesearchR/www/style.css +++ b/inst/apps/FreesearchR/www/style.css @@ -124,4 +124,94 @@ color: #FFFFFF; } +/* Flip-down flip-up */ +.smart-dropdown .selectize-dropdown { + position: absolute; +} + + /* When near bottom of viewport, flip up */ +.flip-up .selectize-dropdown { + top: auto !important; + bottom: 100% !important; + margin-bottom: 5px; + margin-top: 0; +} + + +/* Text-like select input */ +.text-select .control-label { + display: none !important; +} + +.text-select .form-group.shiny-input-container { + height: 24px !important; + min-height: 24px !important; + max-height: 24px !important; + margin: 2px !important; + padding: 2px !important; + display: inline-block; + vertical-align: top; + overflow: visible; +} + +.text-select { + display: inline-block; + vertical-align: top; + position: relative; + top: 0; +} + +.text-select .selectize-control { + margin: 0; + width: 140px !important; + padding: 0; + display: inline-block; + vertical-align: top; +} + +.text-select .selectize-input { + border: none !important; + box-shadow: none !important; + background: transparent !important; + width: 100% !important; + min-width: auto !important; + padding: 0 18px 0 2px !important; + margin: 0 !important; + height: 1em !important; + line-height: 1em !important; + font-size: inherit !important; + font-family: inherit !important; + color: inherit !important; + min-height: 1em !important; + max-height: 1em !important; + display: inline-block !important; + vertical-align: top !important; + position: relative; + top: 0; +} + +.text-select .selectize-input > div { + line-height: 1em !important; + height: 1em !important; + margin: 0 !important; + padding: 0 !important; + vertical-align: top !important; +} + +.text-select .selectize-input::after { + content: '▼'; + position: absolute; + right: 3px; + top: 0; + font-size: 0.8em; + color: #666; + pointer-events: none; + line-height: 1em; +} + +.text-select .form-group { + margin: 0 !important; + display: inline-block; + vertical-align: top; +}