Compare commits

..

No commits in common. "0f0e3ddc13a135d270e61affd590df094e6ababf" and "111c0663bcd2f3d8356f89e908f80c2fe7401fd2" have entirely different histories.

15 changed files with 43 additions and 395 deletions

View file

@ -9,7 +9,7 @@ type: software
license: AGPL-3.0-or-later
title: 'FreesearchR: A free and open-source browser based data analysis tool for researchers
with publication ready output'
version: 25.6.2
version: 25.6.1
doi: 10.5281/zenodo.14527429
identifiers:
- type: url

View file

@ -1,6 +1,6 @@
Package: FreesearchR
Title: A free and open-source browser based data analysis tool for researchers with publication ready output
Version: 25.6.3
Version: 25.6.1
Authors@R: c(
person("Andreas Gammelgaard", "Damsbo",email="agdamsbo@clin.au.dk", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-7559-1154")),
@ -64,8 +64,7 @@ Imports:
RcppArmadillo,
ggcorrplot,
shinyjs,
emmeans,
visdat
emmeans
Suggests:
styler,
devtools,

View file

@ -30,8 +30,6 @@ export(cut_variable_ui)
export(data_correlations_server)
export(data_correlations_ui)
export(data_description)
export(data_missings_server)
export(data_missings_ui)
export(data_summary_server)
export(data_summary_ui)
export(data_type)

14
NEWS.md
View file

@ -1,17 +1,3 @@
# FreesearchR 25.6.3
- *NEW* First go at introducing more options to evaluate missings. Also reworded the text on the initial filter to only include variables missings less than the given threshold.
# FreesearchR 25.6.2
- *FIX* Added warning about only using REDCap with sensitive data running locally. THis applies to all data actually. Considering taking REDCap out in hosted version. Standalone app is in the works.
- *FIX* Reworded the completeness filter to be on missingness, as this is a more commonly used concept.
- *FIX* Improved layout around data filters to improve usage.
- *FIX* Regression table in report respects inclusion of p-values or not.
# FreesearchR 25.6.1
- *FIX* big not allowing to browse data

View file

@ -1 +1 @@
app_version <- function()'25.6.3'
app_version <- function()'25.6.1'

View file

@ -50,7 +50,7 @@ write_quarto <- function(data, ...) {
)
}
write_rmd <- function(data, ..., params.args=NULL) {
write_rmd <- function(data, ...) {
# Exports data to temporary location
#
# I assume this is more secure than putting it in the www folder and deleting
@ -65,7 +65,7 @@ write_rmd <- function(data, ..., params.args=NULL) {
## Ref: https://github.com/quarto-dev/quarto-cli/discussions/4041
## Outputs to the same as the .qmd file
rmarkdown::render(
params = modifyList(list(data.file = "web_data.rds",version=app_version()),params.args),
params = list(data.file = "web_data.rds",version=app_version()),
# execute_params = list(data.file = temp),
...
)

View file

@ -1 +1 @@
hosted_version <- function()'v25.6.3-250620'
hosted_version <- function()'v25.6.1-250604'

View file

@ -1,113 +0,0 @@
#' Data correlations evaluation module
#'
#' @param id Module id
#'
#' @name data-missings
#' @returns Shiny ui module
#' @export
data_missings_ui <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
gt::gt_output(outputId = ns("missings_table")),
shiny::plotOutput(outputId = ns("missings_plot"))
)
}
#'
#' @param data data
#' @param output.format output format
#'
#' @name data-missings
#' @returns shiny server module
#' @export
data_missings_server <- function(id,
data,
...) {
shiny::moduleServer(
id = id,
module = function(input, output, session) {
# ns <- session$ns
rv <- shiny::reactiveValues(
data = NULL
)
rv$data <- if (is.reactive(data)) data else reactive(data)
output$missings_plot <- shiny::renderPlot({
visdat::vis_dat(rv$data(),palette = "cb_safe")
})
}
)
}
missing_demo_app <- function() {
ui <- shiny::fluidPage(
shiny::actionButton(
inputId = "modal_missings",
label = "Browse data",
width = "100%",
disabled = FALSE
)#,
# data_missings_ui("data")
)
server <- function(input, output, session) {
data_demo <- mtcars
data_demo[2:4, "cyl"] <- NA
observeEvent(input$modal_missings, {
tryCatch(
{
modal_data_missings(data = data_demo, id = "modal_missings")
},
error = function(err) {
showNotification(paste0("We encountered the following error browsing your data: ", err), type = "err")
}
)
})
}
shiny::shinyApp(ui, server)
}
missing_demo_app()
modal_data_missings <- function(data,
title = "Show missing pattern",
easyClose = TRUE,
size = "xl",
footer = NULL,
...) {
datar <- if (is.reactive(data)) data else reactive(data)
showModal(modalDialog(
title = tagList(title, datamods:::button_close_modal()),
tags$div(
shiny::renderPlot({
visdat::vis_dat(datar())+
ggplot2::guides(fill = ggplot2::guide_legend(title = "Data class")) +
# ggplot2::theme_void() +
ggplot2::theme(
# legend.position = "none",
panel.grid.major = ggplot2::element_blank(),
panel.grid.minor = ggplot2::element_blank(),
# axis.text.y = element_blank(),
# axis.title.y = element_blank(),
text = ggplot2::element_text(size = 15),
# axis.text = ggplot2::element_blank(),
# panel.background = ggplot2::element_rect(fill = "white"),
# plot.background = ggplot2::element_rect(fill = "white"),
# panel.border = ggplot2::element_blank()
plot.title = ggplot2::element_blank()
)
})
),
easyClose = easyClose,
size = size,
footer = footer
))
}

Binary file not shown.

View file

@ -11,11 +11,11 @@
|collate |en_US.UTF-8 |
|ctype |en_US.UTF-8 |
|tz |Europe/Copenhagen |
|date |2025-06-06 |
|date |2025-06-04 |
|rstudio |2025.05.0+496 Mariposa Orchid (desktop) |
|pandoc |3.6.4 @ /opt/homebrew/bin/ (via rmarkdown) |
|quarto |1.7.30 @ /usr/local/bin/quarto |
|FreesearchR |25.6.2.250606 |
|FreesearchR |25.6.1.250604 |
--------------------------------------------------------------------------------
@ -76,6 +76,7 @@
|farver |2.1.2 |2024-05-13 |CRAN (R 4.4.1) |
|fastmap |1.2.0 |2024-05-15 |CRAN (R 4.4.1) |
|flextable |0.9.7 |2024-10-27 |CRAN (R 4.4.1) |
|fontawesome |0.5.3 |2024-11-16 |CRAN (R 4.4.1) |
|fontBitstreamVera |0.1.1 |2017-02-01 |CRAN (R 4.4.1) |
|fontLiberation |0.1.0 |2016-10-15 |CRAN (R 4.4.1) |
|fontquiver |0.2.1 |2017-02-01 |CRAN (R 4.4.0) |
@ -83,7 +84,7 @@
|foreach |1.5.2 |2022-02-02 |CRAN (R 4.4.0) |
|foreign |0.8-90 |2025-03-31 |CRAN (R 4.4.1) |
|Formula |1.2-5 |2023-02-24 |CRAN (R 4.4.1) |
|FreesearchR |25.6.2 |NA |NA |
|FreesearchR |25.6.1 |NA |NA |
|fs |1.6.6 |2025-04-12 |CRAN (R 4.4.1) |
|gdtools |0.4.2 |2025-03-27 |CRAN (R 4.4.1) |
|generics |0.1.3 |2022-07-05 |CRAN (R 4.4.1) |
@ -159,7 +160,6 @@
|R6 |2.6.1 |2025-02-15 |CRAN (R 4.4.1) |
|ragg |1.4.0 |2025-04-10 |CRAN (R 4.4.1) |
|rankinPlot |1.1.0 |2023-01-30 |CRAN (R 4.4.0) |
|rappdirs |0.3.3 |2021-01-31 |CRAN (R 4.4.1) |
|rbibutils |2.3 |2024-10-04 |CRAN (R 4.4.1) |
|RColorBrewer |1.1-3 |2022-04-03 |CRAN (R 4.4.1) |
|Rcpp |1.0.14 |2025-01-12 |CRAN (R 4.4.1) |

View file

@ -16,7 +16,7 @@ library(shiny)
# library(readODS)
# library(bslib)
# library(assertthat)
library(dplyr)
# library(dplyr)
# library(quarto)
# library(here)
# library(broom)
@ -31,7 +31,7 @@ library(datamods)
library(shinyWidgets)
# library(DT)
# library(data.table)
library(gtsummary)
# library(gtsummary)
library(bsicons)
library(rlang)
# library(datamods)
@ -49,7 +49,7 @@ library(rlang)
#### Current file: /Users/au301842/FreesearchR/R//app_version.R
########
app_version <- function()'25.6.3'
app_version <- function()'25.6.1'
########
@ -3374,7 +3374,7 @@ write_quarto <- function(data, ...) {
)
}
write_rmd <- function(data, ..., params.args=NULL) {
write_rmd <- function(data, ...) {
# Exports data to temporary location
#
# I assume this is more secure than putting it in the www folder and deleting
@ -3389,7 +3389,7 @@ write_rmd <- function(data, ..., params.args=NULL) {
## Ref: https://github.com/quarto-dev/quarto-cli/discussions/4041
## Outputs to the same as the .qmd file
rmarkdown::render(
params = modifyList(list(data.file = "web_data.rds",version=app_version()),params.args),
params = list(data.file = "web_data.rds",version=app_version()),
# execute_params = list(data.file = temp),
...
)
@ -3996,7 +3996,7 @@ simple_snake <- function(data){
#### Current file: /Users/au301842/FreesearchR/R//hosted_version.R
########
hosted_version <- function()'v25.6.3-250620'
hosted_version <- function()'v25.6.1-250604'
########
@ -4655,125 +4655,6 @@ launch_FreesearchR <- function(...){
########
#### Current file: /Users/au301842/FreesearchR/R//missings-module.R
########
#' Data correlations evaluation module
#'
#' @param id Module id
#'
#' @name data-missings
#' @returns Shiny ui module
#' @export
data_missings_ui <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
gt::gt_output(outputId = ns("missings_table")),
shiny::plotOutput(outputId = ns("missings_plot"))
)
}
#'
#' @param data data
#' @param output.format output format
#'
#' @name data-missings
#' @returns shiny server module
#' @export
data_missings_server <- function(id,
data,
...) {
shiny::moduleServer(
id = id,
module = function(input, output, session) {
# ns <- session$ns
rv <- shiny::reactiveValues(
data = NULL
)
rv$data <- if (is.reactive(data)) data else reactive(data)
output$missings_plot <- shiny::renderPlot({
visdat::vis_dat(rv$data(),palette = "cb_safe")
})
}
)
}
missing_demo_app <- function() {
ui <- shiny::fluidPage(
shiny::actionButton(
inputId = "modal_missings",
label = "Browse data",
width = "100%",
disabled = FALSE
)#,
# data_missings_ui("data")
)
server <- function(input, output, session) {
data_demo <- mtcars
data_demo[2:4, "cyl"] <- NA
observeEvent(input$modal_missings, {
tryCatch(
{
modal_data_missings(data = data_demo, id = "modal_missings")
},
error = function(err) {
showNotification(paste0("We encountered the following error browsing your data: ", err), type = "err")
}
)
})
}
shiny::shinyApp(ui, server)
}
missing_demo_app()
modal_data_missings <- function(data,
title = "Show missing pattern",
easyClose = TRUE,
size = "xl",
footer = NULL,
...) {
datar <- if (is.reactive(data)) data else reactive(data)
showModal(modalDialog(
title = tagList(title, datamods:::button_close_modal()),
tags$div(
shiny::renderPlot({
visdat::vis_dat(datar())+
ggplot2::guides(fill = ggplot2::guide_legend(title = "Data class")) +
# ggplot2::theme_void() +
ggplot2::theme(
# legend.position = "none",
panel.grid.major = ggplot2::element_blank(),
panel.grid.minor = ggplot2::element_blank(),
# axis.text.y = element_blank(),
# axis.title.y = element_blank(),
text = ggplot2::element_text(size = 15),
# axis.text = ggplot2::element_blank(),
# panel.background = ggplot2::element_rect(fill = "white"),
# plot.background = ggplot2::element_rect(fill = "white"),
# panel.border = ggplot2::element_blank()
plot.title = ggplot2::element_blank()
)
})
),
easyClose = easyClose,
size = size,
footer = footer
))
}
########
#### Current file: /Users/au301842/FreesearchR/R//plot_box.R
########
@ -9523,7 +9404,6 @@ ui_elements <- list(
width = 8,
shiny::h4("Choose your data source"),
shiny::br(),
# shiny::uiOutput(outputId = "source"),
shinyWidgets::radioGroupButtons(
inputId = "source",
selected = "file",
@ -9549,13 +9429,6 @@ ui_elements <- list(
),
shiny::conditionalPanel(
condition = "input.source=='redcap'",
shinyWidgets::alert(
id = "redcap-warning",
status = "info",
shiny::tags$h2(shiny::markdown("Careful with sensitive data")),
shiny::tags$p("The", shiny::tags$i(shiny::tags$b("FreesearchR")), "app only stores data for analyses, but please only use with sensitive data when running locally.", "", shiny::tags$a("Read more here", href = "https://agdamsbo.github.io/FreesearchR/#run-locally-on-your-own-machine"),"."),
dismissible = TRUE
),
m_redcap_readUI(
id = "redcap_import",
title = ""
@ -9573,11 +9446,11 @@ ui_elements <- list(
condition = "output.data_loaded == true",
shiny::br(),
shiny::br(),
shiny::h5("Select variables for final import"),
shiny::h5("Specify variables to include"),
shiny::fluidRow(
shiny::column(
width = 6,
shiny::p("Exclude incomplete variables:"),
shiny::p("Filter by completeness threshold:"),
shiny::br(),
shinyWidgets::noUiSliderInput(
inputId = "complete_cutoff",
@ -9586,16 +9459,16 @@ ui_elements <- list(
min = 0,
max = 100,
step = 5,
value = 30,
value = 70,
format = shinyWidgets::wNumbFormat(decimals = 0),
color = datamods:::get_primary_color()
),
shiny::helpText("Only include variables with missingness below the specified percentage."),
shiny::helpText("Exclude variables with completeness below the specified percentage."),
shiny::br()
),
shiny::column(
width = 6,
shiny::p("Manual selection:"),
shiny::p("Specify manually:"),
shiny::br(),
shiny::uiOutput(outputId = "import_var"),
shiny::br()
@ -9655,45 +9528,34 @@ ui_elements <- list(
),
shiny::column(
width = 3,
shiny::actionButton(
inputId = "modal_missings",
label = "Visual overview",
width = "100%",
disabled = TRUE
),
shiny::br(),
shiny::br(),
shiny::actionButton(
inputId = "modal_browse",
label = "Browse data",
width = "100%",
disabled = TRUE
),
shiny::br(),
shiny::br(),
shiny::tags$h6("Filter data types"),
shiny::uiOutput(
outputId = "column_filter"),
shiny::helpText("Read more on how ", tags$a(
"data types",
shiny::tags$br(),
shiny::tags$br(),
shiny::uiOutput(outputId = "column_filter"),
shiny::helpText("Variable ", tags$a(
"data type",
href = "https://agdamsbo.github.io/FreesearchR/articles/data-types.html",
target = "_blank",
rel = "noopener noreferrer"
), " are defined."),
shiny::br(),
shiny::br(),
shiny::tags$h6("Create data filters"),
shiny::tags$p("Filter on observation level"),
), " filtering."),
shiny::tags$br(),
shiny::tags$br(),
IDEAFilter::IDEAFilter_ui("data_filter"),
shiny::br(),
shiny::br()
shiny::helpText("Observations level filtering."),
shiny::tags$br(),
shiny::tags$br()
)
),
shiny::br(),
shiny::br(),
# shiny::br(),
# shiny::br(),
shiny::br()
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br()
),
bslib::nav_panel(
title = "Modify",
@ -10091,15 +9953,9 @@ ui <- bslib::page_fixed(
data(starwars)
data(mtcars)
# trial <- gtsummary::trial
# starwars <- dplyr::starwars
#
# mtcars_na <- rbind(mtcars,NA,NA)
# thematic::thematic_shiny()
data(trial)
load_data <- function() {
Sys.sleep(1)
@ -10107,7 +9963,6 @@ load_data <- function() {
shinyjs::show("main_content")
}
# is_local = is.na(Sys.getenv('SHINY_SERVER_VERSION', NA))
server <- function(input, output, session) {
## Listing files in www in session start to keep when ending and removing
@ -10163,28 +10018,6 @@ server <- function(input, output, session) {
#########
##############################################################################
## This does not render correctly apparently due to css and load order
# output$source <- shiny::renderUI({
#
# choices <- c(
# "File upload" = "file",
# "REDCap server export" = "redcap",
# "Local or sample data" = "env"
# )
#
# if (isTRUE(is_local)){
# choices <- choices[c(1,3)]
# }
#
# shinyWidgets::radioGroupButtons(
# inputId = "source",
# selected = "file",
# choices = choices,
# size = "lg"
# )
# })
data_file <- import_file_server(
id = "file_import",
show_data_in = "popup",
@ -10234,7 +10067,7 @@ server <- function(input, output, session) {
output$import_var <- shiny::renderUI({
shiny::req(rv$data_temp)
preselect <- names(rv$data_temp)[sapply(rv$data_temp, missing_fraction) <= (input$complete_cutoff / 100)]
preselect <- names(rv$data_temp)[sapply(rv$data_temp, missing_fraction) <= input$complete_cutoff / 100]
shinyWidgets::virtualSelectInput(
inputId = "import_var",
@ -10303,12 +10136,10 @@ server <- function(input, output, session) {
if (is.null(rv$data_original) | NROW(rv$data_original) == 0) {
shiny::updateActionButton(inputId = "act_start", disabled = TRUE)
shiny::updateActionButton(inputId = "modal_browse", disabled = TRUE)
shiny::updateActionButton(inputId = "modal_missings", disabled = TRUE)
shiny::updateActionButton(inputId = "act_eval", disabled = TRUE)
} else {
shiny::updateActionButton(inputId = "act_start", disabled = FALSE)
shiny::updateActionButton(inputId = "modal_browse", disabled = FALSE)
shiny::updateActionButton(inputId = "modal_missings", disabled = FALSE)
shiny::updateActionButton(inputId = "act_eval", disabled = FALSE)
}
})
@ -10448,7 +10279,7 @@ server <- function(input, output, session) {
# c("dichotomous", "ordinal", "categorical", "datatime", "continuous")
shinyWidgets::virtualSelectInput(
inputId = "column_filter",
label = "Select data types to include",
label = "Select variable types to include",
selected = unique(data_type(rv$data)),
choices = unique(data_type(rv$data)),
updateOn = "change",
@ -10547,19 +10378,6 @@ server <- function(input, output, session) {
)
})
observeEvent(input$modal_missings, {
tryCatch(
{
modal_data_missings(data = REDCapCAST::fct_drop(rv$data_filtered),
footer = "This pop-up gives you an overview of how your data is interpreted, and where data is missing. Use this information to consider if data is missing at random or if some observations are missing systematically wich may be caused by an observation bias.")
},
error = function(err) {
showNotification(paste0("We encountered the following error showing missingness: ", err), type = "err")
}
)
})
output$original_str <- renderPrint({
str(rv$data_original)
})
@ -10842,9 +10660,6 @@ server <- function(input, output, session) {
{
rv$list |>
write_rmd(
params.args = list(
regression.p=rv$list$regression$input$add_regression_p
),
output_format = format,
input = file.path(getwd(), "www/report.rmd")
)

View file

@ -7,7 +7,6 @@ toc: false
params:
data.file: NA
version: NA
regression.p: NA
---
```{r setup, echo = FALSE}
@ -66,15 +65,7 @@ if ("table1" %in% names(web_data)) {
```{r, results = 'asis'}
if ("regression" %in% names(web_data) && length(web_data$regression) > 0) {
reg_tbl <- web_data$regression$regression$tables
merged <- tbl_merge(reg_tbl)
if (params$regression.p == "no") {
merged <- merged |>
gtsummary::modify_column_hide(column = dplyr::starts_with("p.value"))
}
knitr::knit_print(merged)
knitr::knit_print(tbl_merge(reg_tbl))
}
```

View file

@ -1 +0,0 @@
<script defer src="https://stats.freesearchr.org/script.js" data-website-id="349608b9-78f8-47ee-9185-0d3716095fd5"></script>

View file

@ -1,27 +0,0 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/missings-module.R
\name{data-missings}
\alias{data-missings}
\alias{data_missings_ui}
\alias{data_missings_server}
\title{Data correlations evaluation module}
\usage{
data_missings_ui(id)
data_missings_server(id, data, ...)
}
\arguments{
\item{id}{Module id}
\item{data}{data}
\item{output.format}{output format}
}
\value{
Shiny ui module
shiny server module
}
\description{
Data correlations evaluation module
}