From 810696cc26032a6917308f08c312201894b16c63 Mon Sep 17 00:00:00 2001 From: Lukas Wallrich Date: Sun, 1 Feb 2026 18:00:11 +0000 Subject: [PATCH 1/2] Add load latest data button, quiet mode default, and fetch citation from GitHub - Add "Load latest data" button to Explorer sidebar that fetches fresh data from OSF (closes #85) - Change verbose default to FALSE in load_fred_data() and read_fred() for quieter output (closes #53) - Modify create_citation() to fetch citation from GitHub instead of regenerating (closes #98) Co-Authored-By: Claude Opus 4.5 --- R/data_management.R | 4 +-- R/fred.R | 27 ++++++----------- inst/fred_explorer/global.R | 17 +++++++---- inst/fred_explorer/server.R | 58 ++++++++++++++++++++++++++++++------- inst/fred_explorer/ui.R | 5 +++- man/create_citation.Rd | 14 +++++---- man/load_fred_data.Rd | 4 +-- man/read_fred.Rd | 4 +-- 8 files changed, 88 insertions(+), 45 deletions(-) diff --git a/R/data_management.R b/R/data_management.R index f32bd4d..1ea0321 100644 --- a/R/data_management.R +++ b/R/data_management.R @@ -85,10 +85,10 @@ bind_rows_with_characters <- function(..., .id = NULL) { #' #' @param data Path to the FReD dataset (defaults to current FReD data on OSF), unless the package is in offline mode (`use_FReD_offline()`) #' @param retain_es_as_character Should effect sizes be retained as character? Defaults to TRUE, so that coded test statistics with df can be converted to common metric. -#' @param verbose Should detailed messages be printed that highlight data conversion issues? Defaults to TRUE. FALSE is quiet mode, and NULL prints a summary of problems. +#' @param verbose Should detailed messages be printed that highlight data conversion issues? Defaults to FALSE (quiet mode). TRUE prints detailed warnings, and NULL prints a summary of problems. #' @return A data frame with the FReD dataset -read_fred <- function(data = get_param("FRED_DATA_FILE"), retain_es_as_character = TRUE, verbose = TRUE) { +read_fred <- function(data = get_param("FRED_DATA_FILE"), retain_es_as_character = TRUE, verbose = FALSE) { if (get_param("FRED_OFFLINE")) return(return_inbuilt("data")) diff --git a/R/fred.R b/R/fred.R index 84a0e14..8aaa0cc 100644 --- a/R/fred.R +++ b/R/fred.R @@ -59,15 +59,15 @@ dummy_function_calls <- function() { } } -#' Create FReD dataset citation +#' Get FReD dataset citation #' -#' Pulls current contributor list and dynamicalky creates a *markdown-formatted* citation for the FReD dataset. +#' Retrieves the current citation for the FReD dataset from GitHub. #' -#' @param data_file Path to the FReD dataset, defaults to the current FReD dataset on OSF +#' @param citation_url URL to the citation file on GitHub #' @param cache Should the citation be returned from cache, if already requested during this session? Defaults to TRUE. -#' @return A markdown-formatted citation for the FReD dataset, including the current dataset version. +#' @return A markdown-formatted citation for the FReD dataset. -create_citation <- function(data_file = get_param("FRED_DATA_FILE"), cache = TRUE) { +create_citation <- function(citation_url = "https://raw.githubusercontent.com/forrtproject/FReD-data/main/output/citation.txt", cache = TRUE) { if (get_param("FRED_OFFLINE")) { return(return_inbuilt("citation")) } @@ -77,19 +77,10 @@ create_citation <- function(data_file = get_param("FRED_DATA_FILE"), cache = TRU if (cache && exists("citation", .cache, inherits = FALSE)) { return(.cache$citation) } - contributors <- safe_read_xl(data_file, url = get_param("FRED_DATA_URL"), sheet = "Contributors FReD") - contributors <- contributors[contributors$Added.to.FReD.website.as.contributor, ] - contributors$first <- substr(contributors$First.name, 1, 1) - contributors$middle <- ifelse(!is.na(contributors$Middle.name), paste(" ", substr(contributors$Middle.name, 1, 1), ".", sep = ""), "") - contributors$apa <- paste0( - contributors$Surname, ", ", - contributors$first, ".", - contributors$middle - ) - c_names <- paste(contributors$apa, collapse = ", ") - version <- get_dataset_changelog() %>% stringr::str_extract("(?<=\\*\\*Version:\\*\\* )\\d+\\.\\d+\\.\\d+") - cit <- glue::glue("{c_names} (2024). _FReD: FORRT Replication Database, version {version}._ [https://dx.doi.org/10.17605/OSF.IO/9r62x] _*shared first authorship_") + temp <- tempfile(fileext = ".txt") + download.file(citation_url, temp, quiet = TRUE) + cit <- readLines(temp, warn = FALSE) %>% paste(collapse = "\n") .cache$citation <- cit @@ -138,7 +129,7 @@ get_dataset_changelog <- function(changelog_file = "https://osf.io/fj3xc/downloa #' @return A data frame with the processed FReD dataset #' @export -load_fred_data <- function(data = get_param("FRED_DATA_FILE"), verbose = TRUE) { +load_fred_data <- function(data = get_param("FRED_DATA_FILE"), verbose = FALSE) { read_fred(data, verbose = verbose) %>% clean_variables() %>% diff --git a/inst/fred_explorer/global.R b/inst/fred_explorer/global.R index 305ed0a..4a03694 100644 --- a/inst/fred_explorer/global.R +++ b/inst/fred_explorer/global.R @@ -12,12 +12,19 @@ if (!exists("create_citation")) { if (!exists("create_citation")) stop("Failed to attach FReD namespace.") -df <- load_fred_data() - -df_display <- df[, c("description", "es_o", "es_r", "n_o", "n_r", "osf_link", "contributors", "result", "result2", "ref_o", "ref_r")] -df_display$es_o <- round(df_display$es_o, 3) -df_display$es_r <- round(df_display$es_r, 3) +# Function to load and prepare data - can be called again to refresh +load_app_data <- function() { + df <- load_fred_data() + df_display <- df[, c("description", "es_o", "es_r", "n_o", "n_r", "osf_link", "contributors", "result", "result2", "ref_o", "ref_r")] + df_display$es_o <- round(df_display$es_o, 3) + df_display$es_r <- round(df_display$es_r, 3) + list(df = df, df_display = df_display) +} +# Initial data load +app_data <- load_app_data() +df <- app_data$df +df_display <- app_data$df_display dataset_variables <- load_variable_descriptions() diff --git a/inst/fred_explorer/server.R b/inst/fred_explorer/server.R index 7ce1cc1..3f21cf5 100644 --- a/inst/fred_explorer/server.R +++ b/inst/fred_explorer/server.R @@ -9,7 +9,39 @@ server <- function(input, output, session) { } }) + # Reactive data source - allows refreshing + data_source <- reactiveValues( + df = df, + df_display = df_display + ) + + # Refresh data when button is clicked + + observeEvent(input$refresh_data, { + showNotification("Refreshing data from source...", type = "message", duration = 2) + tryCatch({ + # Force fresh download by temporarily disabling offline mode + was_offline <- get_param("FRED_OFFLINE") + if (was_offline) use_FReD_offline(FALSE) + + # Delete cached data file to force fresh download from OSF + cached_file <- get_param("FRED_DATA_FILE") + if (file.exists(cached_file)) { + file.remove(cached_file) + } + + new_data <- load_app_data() + data_source$df <- new_data$df + data_source$df_display <- new_data$df_display + if (was_offline) use_FReD_offline(TRUE) + + showNotification(paste("Data refreshed successfully!", nrow(new_data$df), "findings loaded."), + type = "message", duration = 5) + }, error = function(e) { + showNotification(paste("Error refreshing data:", e$message), type = "error", duration = 5) + }) + }) # Disclaimer -------------------------------------------------------------- @@ -29,7 +61,7 @@ server <- function(input, output, session) { # Update df_temp based on filters observe({ - df_temp <- df[rev(row.names(df)), ] + df_temp <- data_source$df[rev(row.names(data_source$df)), ] # source if (input$source == "All studies") { @@ -410,7 +442,7 @@ server <- function(input, output, session) { output$dataset <- DT::renderDT(server = FALSE, - DT::datatable(df_display, + DT::datatable(data_source$df_display, rownames = FALSE, # extensions = 'Buttons', options = list(scrollX=TRUE, lengthMenu = c(5, 10, 15), @@ -660,14 +692,17 @@ server <- function(input, output, session) { entries <- unlist(base::strsplit(entries, split = "\n")) # |- dois <- tolower(stringr::str_extract(entries, "10.\\d{4,9}/[-._;()/:a-z0-9A-Z]+")) + # Use local copy to avoid modifying reactive data + df_local <- data_source$df + # combine coded and uncoded studies - df[is.na(df$result), "result"] <- "Not coded yet" + df_local[is.na(df_local$result), "result"] <- "Not coded yet" # Check which entries exist in the df - intersection <- dois[dois %in% df$doi_o] + intersection <- dois[dois %in% df_local$doi_o] # df subset - df_temp <- df[(tolower(df$doi_o) %in% dois), ] + df_temp <- df_local[(tolower(df_local$doi_o) %in% dois), ] df_temp <- df_temp[!is.na(df_temp$doi_o), ] bardata <- as.data.frame(base::table(df_temp$result, useNA = "always") / nrow(df_temp)) @@ -698,14 +733,17 @@ server <- function(input, output, session) { entries <- unlist(base::strsplit(entries, split = "\n")) # |- dois <- tolower(stringr::str_extract(entries, "10.\\d{4,9}/[-._;()/:a-z0-9A-Z]+")) + # Use local copy to avoid modifying reactive data + df_local <- data_source$df + # combine coded and uncoded studies - df[is.na(df$result), "result"] <- "Not coded yet" + df_local[is.na(df_local$result), "result"] <- "Not coded yet" # Check which entries exist in the df - intersection <- dois[dois %in% df$doi_o] + intersection <- dois[dois %in% df_local$doi_o] # df subset - df_temp <- df[(tolower(df$doi_o) %in% dois), ] + df_temp <- df_local[(tolower(df_local$doi_o) %in% dois), ] df_temp <- df_temp[!is.na(df_temp$doi_o), ] df_temp$original <- df_temp$ref_o # paste(df_temp$ref_o, df_temp$doi_o, sep = " ") # ADD DOIs if they are not already part of the reference @@ -884,7 +922,7 @@ server <- function(input, output, session) { output$checker_bar <- plotly::renderPlotly({ # this plot is based on the filtered entries from the checkertable - df_temp <- df + df_temp <- data_source$df df_temp <- df_temp[rev(row.names(df_temp)), ] # exclude non-validated entries @@ -929,7 +967,7 @@ server <- function(input, output, session) { output$flexiblecheckertable <- DT::renderDT({ # this plot is based on the filtered entries from the checkertable - df_temp <- df + df_temp <- data_source$df df_temp <- df_temp[rev(row.names(df_temp)), ] # exclude non-validated entries diff --git a/inst/fred_explorer/ui.R b/inst/fred_explorer/ui.R index 203cee2..4bed67c 100644 --- a/inst/fred_explorer/ui.R +++ b/inst/fred_explorer/ui.R @@ -84,7 +84,10 @@ sidebar_contents <- sidebar( selected = "significance_r"), div( HTML("NB: The success criteria (e.g., p-values, CIs) are calculated from raw effect and sample sizes. These may differ from original reports that used adjusted models.") - ) + ), + hr(), + actionButton("refresh_data", "Load latest data", icon = icon("refresh"), + style = "width: 100%;") ) # Define content for each panel diff --git a/man/create_citation.Rd b/man/create_citation.Rd index 137c17f..7f9cabf 100644 --- a/man/create_citation.Rd +++ b/man/create_citation.Rd @@ -2,18 +2,22 @@ % Please edit documentation in R/fred.R \name{create_citation} \alias{create_citation} -\title{Create FReD dataset citation} +\title{Get FReD dataset citation} \usage{ -create_citation(data_file = get_param("FRED_DATA_FILE"), cache = TRUE) +create_citation( + citation_url = + "https://raw.githubusercontent.com/forrtproject/FReD-data/main/output/citation.txt", + cache = TRUE +) } \arguments{ -\item{data_file}{Path to the FReD dataset, defaults to the current FReD dataset on OSF} +\item{citation_url}{URL to the citation file on GitHub} \item{cache}{Should the citation be returned from cache, if already requested during this session? Defaults to TRUE.} } \value{ -A markdown-formatted citation for the FReD dataset, including the current dataset version. +A markdown-formatted citation for the FReD dataset. } \description{ -Pulls current contributor list and dynamicalky creates a \emph{markdown-formatted} citation for the FReD dataset. +Retrieves the current citation for the FReD dataset from GitHub. } diff --git a/man/load_fred_data.Rd b/man/load_fred_data.Rd index 290a706..102313e 100644 --- a/man/load_fred_data.Rd +++ b/man/load_fred_data.Rd @@ -4,12 +4,12 @@ \alias{load_fred_data} \title{Load the FReD dataset} \usage{ -load_fred_data(data = get_param("FRED_DATA_FILE"), verbose = TRUE) +load_fred_data(data = get_param("FRED_DATA_FILE"), verbose = FALSE) } \arguments{ \item{data}{Path to the FReD dataset (defaults to current FReD data on OSF), unless the package is in offline mode (\code{use_FReD_offline()})} -\item{verbose}{Should detailed messages be printed that highlight data conversion issues? Defaults to TRUE. FALSE is quiet mode, and NULL prints a summary of problems.} +\item{verbose}{Should detailed messages be printed that highlight data conversion issues? Defaults to FALSE (quiet mode). TRUE prints detailed warnings, and NULL prints a summary of problems.} } \value{ A data frame with the processed FReD dataset diff --git a/man/read_fred.Rd b/man/read_fred.Rd index 8d08b0c..e35ed9d 100644 --- a/man/read_fred.Rd +++ b/man/read_fred.Rd @@ -7,7 +7,7 @@ read_fred( data = get_param("FRED_DATA_FILE"), retain_es_as_character = TRUE, - verbose = TRUE + verbose = FALSE ) } \arguments{ @@ -15,7 +15,7 @@ read_fred( \item{retain_es_as_character}{Should effect sizes be retained as character? Defaults to TRUE, so that coded test statistics with df can be converted to common metric.} -\item{verbose}{Should detailed messages be printed that highlight data conversion issues? Defaults to TRUE. FALSE is quiet mode, and NULL prints a summary of problems.} +\item{verbose}{Should detailed messages be printed that highlight data conversion issues? Defaults to FALSE (quiet mode). TRUE prints detailed warnings, and NULL prints a summary of problems.} } \value{ A data frame with the FReD dataset From 111897353e37c538d5c2bc1bf166a69d94f88af8 Mon Sep 17 00:00:00 2001 From: Lukas Wallrich Date: Sun, 1 Feb 2026 20:38:11 +0000 Subject: [PATCH 2/2] Add validation for citation download and fix update_offline_data call - Add validation that citation file was downloaded successfully and is not empty - Fix update_offline_data() to call create_citation() without data_file argument Co-Authored-By: Claude Opus 4.5 --- R/data_management.R | 2 +- R/fred.R | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/R/data_management.R b/R/data_management.R index 1ea0321..1046e6d 100644 --- a/R/data_management.R +++ b/R/data_management.R @@ -288,7 +288,7 @@ update_offline_data <- function(data_file = get_param("FRED_DATA_FILE"), items = } else if (item == "data_changelog") { data <- get_dataset_changelog() } else if (item == "citation") { - data <- create_citation(data_file) + data <- create_citation() } attr(data, "last_updated") <- Sys.time() file_path <- system.file("extdata", "snapshot", paste0(item, ".RDS"), package = "FReD") diff --git a/R/fred.R b/R/fred.R index 8aaa0cc..3e6e08b 100644 --- a/R/fred.R +++ b/R/fred.R @@ -80,8 +80,19 @@ create_citation <- function(citation_url = "https://raw.githubusercontent.com/fo temp <- tempfile(fileext = ".txt") download.file(citation_url, temp, quiet = TRUE) + + # Validate download succeeded + if (!file.exists(temp) || file.size(temp) == 0) { + stop("Failed to download citation file") + } + cit <- readLines(temp, warn = FALSE) %>% paste(collapse = "\n") + # Validate citation content is not empty + if (nchar(trimws(cit)) == 0) { + stop("Citation file is empty") + } + .cache$citation <- cit cit