diff --git a/.Rbuildignore b/.Rbuildignore
index 27758da..d6bf5b2 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -5,3 +5,5 @@
^_pkgdown\.yml$
^docs$
^pkgdown$
+^\.claude$
+^claude\.md$
diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml
index b1f9cd1..f055199 100644
--- a/.github/workflows/R-CMD-check.yaml
+++ b/.github/workflows/R-CMD-check.yaml
@@ -11,6 +11,7 @@ env:
jobs:
document:
+ if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
@@ -47,10 +48,11 @@ jobs:
run: |
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
- git pull
+ git fetch origin ${{ github.ref_name }}
+ git checkout ${{ github.ref_name }}
git add -f man/\* NAMESPACE
- git commit -m 'Documentation' || echo "No changes to commit"
- git push origin || echo "No changes to commit"
+ git commit -m 'Documentation [automated]' || echo "No changes to commit"
+ git push origin ${{ github.ref_name }} || echo "No changes to commit"
R-CMD-check:
if: ${{ always() }}
diff --git a/.github/workflows/document-and-deploy.yml b/.github/workflows/document-and-deploy.yml
index cd0a5c3..c73d731 100644
--- a/.github/workflows/document-and-deploy.yml
+++ b/.github/workflows/document-and-deploy.yml
@@ -40,12 +40,13 @@ jobs:
"
- name: commit
+ if: github.event_name != 'pull_request'
run: |
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add -f man/\* NAMESPACE
- git commit -m 'Documentation' || echo "No changes to commit"
- git push origin || echo "No changes to commit"
+ git commit -m 'Documentation [automated]' || echo "No changes to commit"
+ git push origin HEAD:${{ github.ref_name }} || echo "No changes to commit"
- name: Create pkgdown
env:
@@ -72,7 +73,6 @@ jobs:
R -e "
remotes::install_github('${{ github.repository }}', ref = '${{ github.ref_name }}', force = TRUE);
rsconnect::setAccountInfo(name='forrt-replications', token=${{secrets.SHINYAPPS_TOKEN}}, secret=${{secrets.SHINYAPPS_SECRET}});
- rsconnect::deployApp(appName = 'fred_annotator', appDir = './inst/fred_annotator', forceUpdate = TRUE);
rsconnect::deployApp(appName = 'fred_explorer', appDir = './inst/fred_explorer', forceUpdate = TRUE);
"
@@ -81,5 +81,4 @@ jobs:
run: |
R -e "
rsconnect::deployApp(appName = 'fred_explorer_release', appDir = './inst/fred_explorer', forceUpdate = TRUE);
- rsconnect::deployApp(appName = 'fred_annotator_release', appDir = './inst/fred_annotator', forceUpdate = TRUE);
"
diff --git a/DESCRIPTION b/DESCRIPTION
index 1f4b302..d367332 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: FReD
Title: Interfaces to the FORRT Replication Database
-Version: 0.0.0.9002
+Version: 0.2.0
Authors@R: c(
person("Lukas", "Röseler", , "lukas.roeseler@uni-muenster.de", role = "aut",
comment = c(ORCID = "0000-0002-6446-1901")),
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..075035e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2024
+COPYRIGHT HOLDER: FReD authors
diff --git a/NEWS.md b/NEWS.md
index d06ccca..b16c58f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,15 @@
This version history contains noteworthy changes. For a full history of changes, see the [commit history](https://github.com/forrtproject/FReD/commits/main/)
+# FReD 0.2.0
+
+## Breaking Changes
+- **New data source URL**: The package now uses a new data source with updated variable naming conventions
+- **Variable naming convention**: All variable names now use `_o` suffix for original study variables and `_r` suffix for replication study variables (e.g., `es_original` → `es_o`, `n_replication` → `n_r`, `ref_original` → `ref_o`, `doi_replication` → `doi_r`)
+- **`run_annotator()` deprecated**: The local annotator app has been removed. `run_annotator()` now opens the web version at forrt.org instead
+
+## Notes
+- If you have code that references old variable names, you will need to update it to use the new `_o`/`_r` suffixes
+
# FReD 0.1.0
## New features
diff --git a/R/data_management.R b/R/data_management.R
index 6687012..f32bd4d 100644
--- a/R/data_management.R
+++ b/R/data_management.R
@@ -94,37 +94,26 @@ read_fred <- function(data = get_param("FRED_DATA_FILE"), retain_es_as_character
tryCatch({
- red <- safe_read_xl(data, url = get_param("FRED_DATA_URL"), sheet = "Data") # .xlsx file
- red <- red[-(1:2), ] # exclude labels and "X" column
-
- forrt <- safe_read_xl(data, url = get_param("FRED_DATA_URL"), sheet = "FORRT R&R (editable)", startRow = 1)
- forrt <- forrt[-(1:2), ] # exclude labels and "X" column
- forrt <- forrt[!(forrt$doi_original %in% red$doi_original), ] # exclude forrt entries of original study that already appear in FReD (based on DOIs)
-
- # additional studies
- as <- safe_read_xl(data, url = get_param("FRED_DATA_URL"), sheet = "Additional Studies to be added", startRow = 2)
- as$id <- paste("uncoded_studies_", rownames(as), sep = "")
- as <- as[as$`Study.listed.in.ReD?` != "1.0", ] # exclude additional studies that are already listed in the main dataset
- as <- as[!is.na(as$doi_original), ] # exclude studies for which doi_original is unavailable because they will not be findable in the annotator anyway
-
- numeric_variables <- c("n_original", "n_replication", "es_orig_value", "es_rep_value",
- "validated", "published_rep", "same_design", "same_test",
- "original_authors",
- "significant_original", "significant_replication", "power")
-
-
- if (!retain_es_as_character) {
- numeric_variables <- c(numeric_variables, "es_orig_RRR", "es_rep_RRR")
+ # New data format: single sheet with all data
+ fred_data <- safe_read_xl(data, url = get_param("FRED_DATA_URL"), sheet = 1)
+
+ # Add id column if not present (use fred_id or row number)
+ if (!"id" %in% names(fred_data)) {
+ if ("fred_id" %in% names(fred_data)) {
+ fred_data$id <- fred_data$fred_id
+ } else {
+ fred_data$id <- seq_len(nrow(fred_data))
+ }
}
+ numeric_variables <- c("n_o", "n_r", "es_value_o", "es_value_r", "pval_value_o", "pval_value_r")
+ # Only coerce variables that exist in the dataset
+ numeric_variables <- intersect(numeric_variables, names(fred_data))
- # Assuming 'red' and 'forrt' have a unique ID column named "id"
- red <- coerce_to_numeric(red, numeric_variables, id_var = "id", verbose = verbose)
- forrt <- coerce_to_numeric(forrt, numeric_variables, id_var = "id", verbose = verbose)
+ fred_data <- coerce_to_numeric(fred_data, numeric_variables, id_var = "id", verbose = verbose)
- # merge the data, aligning column types where one is character (as empty colums are imported as numeric)
- return(bind_rows_with_characters(red, forrt, as))
+ return(fred_data)
}, error = function(e) {
return(return_inbuilt("data"))
@@ -149,6 +138,7 @@ coerce_to_numeric <- function(df, numeric_vars, id_var, verbose = TRUE) {
problematic_entries <- list()
for (var in numeric_vars) {
+ # Suppress "NAs introduced by coercion" warnings - we detect and report these ourselves below
problematic_rows <- which(!is.na(df[[var]]) & is.na(suppressWarnings(as.numeric(df[[var]]))))
if (length(problematic_rows) > 0) {
@@ -187,50 +177,75 @@ coerce_to_numeric <- function(df, numeric_vars, id_var, verbose = TRUE) {
clean_variables <- function(fred_data) {
+ # Initialize columns that may not exist in new data format
+ if (!"description" %in% names(fred_data)) fred_data$description <- ""
+ if (!"tags" %in% names(fred_data)) fred_data$tags <- NA
+ if (!"contributors" %in% names(fred_data)) fred_data$contributors <- NA
+ if (!"result" %in% names(fred_data)) fred_data$result <- NA
+ if (!"notes" %in% names(fred_data)) fred_data$notes <- NA
+ if (!"exclusion" %in% names(fred_data)) fred_data$exclusion <- NA
+ if (!"validated" %in% names(fred_data)) fred_data$validated <- 1
+ if (!"osf_link" %in% names(fred_data)) {
+ fred_data$osf_link <- ifelse(!is.na(fred_data$url_r), fred_data$url_r, NA)
+ }
+ if (!"source" %in% names(fred_data)) fred_data$source <- NA
+ if (!"orig_journal" %in% names(fred_data)) {
+ fred_data$orig_journal <- if ("journal_o" %in% names(fred_data)) fred_data$journal_o else NA
+ }
+
# recode variables for app to work
- fred_data$pc_tags <- NA
- fred_data$pc_contributors <- NA
fred_data$description <- ifelse(is.na(fred_data$description), "", fred_data$description)
- fred_data$contributors <- ifelse(is.na(fred_data$contributors), fred_data$pc_contributors, fred_data$contributors)
- fred_data$tags <- ifelse(is.na(fred_data$tags), fred_data$pc_tags, fred_data$tags)
- fred_data$subjects <- NA
- fred_data$description <- ifelse(is.na(fred_data$description), fred_data$pc_title, fred_data$description)
fred_data$closeness <- NA
- fred_data$result <- ifelse(fred_data$result == "0", NA, fred_data$result)
+ fred_data$result <- ifelse(!is.na(fred_data$result) & fred_data$result == "0", NA, fred_data$result)
- fred_data$result
-
- # compute year the original study was published (match 1800-2099 only, and require consecutive numbers)
- fred_data$orig_year <- as.numeric(gsub(".*((18|19|20)\\d{2}).*", "\\1", fred_data$ref_original))
+ # compute year the original study was published - use year_o if available, otherwise extract from ref_o
+ if ("year_o" %in% names(fred_data)) {
+ fred_data$orig_year <- as.numeric(fred_data$year_o)
+ } else {
+ fred_data$orig_year <- as.numeric(gsub(".*((18|19|20)\\d{2}).*", "\\1", fred_data$ref_o))
+ }
- # # delete duplicates and non-replication studies
- fred_data <- fred_data[fred_data$notes != "duplicate" | is.na(fred_data$notes), ] # ADDED: study exclusions due to duplicates
- fred_data <- fred_data[fred_data$notes != "No actual replication conducted" | is.na(fred_data$notes), ] # ADDED: some registrations had no corresponding replication study
+ # delete duplicates and non-replication studies (only if notes column exists and has values)
+ if ("notes" %in% names(fred_data)) {
+ fred_data <- fred_data[is.na(fred_data$notes) | fred_data$notes != "duplicate", ]
+ fred_data <- fred_data[is.na(fred_data$notes) | fred_data$notes != "No actual replication conducted", ]
+ }
- # remove entries with reasons for exclusions
- fred_data <- fred_data[is.na(fred_data$exclusion), ]
+ # remove entries with reasons for exclusions (only if exclusion column exists)
+ if ("exclusion" %in% names(fred_data)) {
+ fred_data <- fred_data[is.na(fred_data$exclusion), ]
+ }
# Collapse validated categories (# 2: error detected and corrected)
- fred_data$validated <- ifelse(fred_data$validated == 1 | fred_data$validated == 2, 1, fred_data$validated)
-
+ if ("validated" %in% names(fred_data)) {
+ fred_data$validated <- ifelse(fred_data$validated == 1 | fred_data$validated == 2, 1, fred_data$validated)
+ }
# Strip DOIs by removing everything before first 10.
- fred_data$doi_original <- gsub("^.*?(10\\.\\d+/.*$)", "\\1", fred_data$doi_original) %>% str_trim_base()
- fred_data$doi_replication <- gsub("^.*?(10\\.\\d+/.*$)", "\\1", fred_data$doi_replication) %>% str_trim_base()
+ if ("doi_o" %in% names(fred_data)) {
+ fred_data$doi_o <- gsub("^.*?(10\\.\\d+/.*$)", "\\1", fred_data$doi_o) %>% str_trim_base()
+ }
+ if ("doi_r" %in% names(fred_data)) {
+ fred_data$doi_r <- gsub("^.*?(10\\.\\d+/.*$)", "\\1", fred_data$doi_r) %>% str_trim_base()
+ }
# Remove DOIs from references
- fred_data$ref_original <- fred_data$ref_original %>%
- stringr::str_remove_all("https?://(dx\\.)?doi\\.org/10\\.[^ >,]+") %>%
- stringr::str_remove_all("doi:10\\.[^ >,]+") %>%
- stringr::str_remove_all("10\\.[^ >,]+") %>%
- str_trim_base()
-
- fred_data$ref_replication <- fred_data$ref_replication %>%
- stringr::str_remove_all("https?://(dx\\.)?doi\\.org/10\\.[^ >,]+") %>%
- stringr::str_remove_all("doi:10\\.[^ >,]+") %>%
- stringr::str_remove_all("10\\.[^ >,]+") %>%
- str_trim_base()
+ if ("ref_o" %in% names(fred_data)) {
+ fred_data$ref_o <- fred_data$ref_o %>%
+ stringr::str_remove_all("https?://(dx\\.)?doi\\.org/10\\.[^ >,]+") %>%
+ stringr::str_remove_all("doi:10\\.[^ >,]+") %>%
+ stringr::str_remove_all("10\\.[^ >,]+") %>%
+ str_trim_base()
+ }
+
+ if ("ref_r" %in% names(fred_data)) {
+ fred_data$ref_r <- fred_data$ref_r %>%
+ stringr::str_remove_all("https?://(dx\\.)?doi\\.org/10\\.[^ >,]+") %>%
+ stringr::str_remove_all("doi:10\\.[^ >,]+") %>%
+ stringr::str_remove_all("10\\.[^ >,]+") %>%
+ str_trim_base()
+ }
fred_data
}
diff --git a/R/effect_size_transformations.R b/R/effect_size_transformations.R
index 932e795..bcc1f00 100644
--- a/R/effect_size_transformations.R
+++ b/R/effect_size_transformations.R
@@ -350,8 +350,8 @@ as_numeric_verbose <- function(x, quiet = FALSE) {
#' @param coalesce_values Logical. Should existing values in es_type_columns be retained?
#' @return FReD dataset with additional columns for common effect sizes
-add_common_effect_sizes <- function(fred_data, es_value_columns = c("es_orig_value", "es_rep_value"),
- es_type_columns = c("es_orig_estype", "es_rep_estype"), es_common_names = c("es_original", "es_replication"),
+add_common_effect_sizes <- function(fred_data, es_value_columns = c("es_value_o", "es_value_r"),
+ es_type_columns = c("es_type_o", "es_type_r"), es_common_names = c("es_o", "es_r"),
coalesce_values = TRUE) {
if (!all.equal(length(es_value_columns), length(es_type_columns), length(es_common_names))) {
stop("Length of es_value_columns, es_type_columns, and es_common_names must be equal")
@@ -399,7 +399,7 @@ add_common_effect_sizes <- function(fred_data, es_value_columns = c("es_orig_val
#' @param es_replication Character. Name of replication effect size column.
#' @return Augmented FReD dataset with aligned effect directions.
-align_effect_direction <- function(fred_data, es_original = "es_original", es_replication = "es_replication") {
+align_effect_direction <- function(fred_data, es_original = "es_o", es_replication = "es_r") {
orig_direction <- sign(fred_data[, es_original])
fred_data[, es_original] <- abs(fred_data[, es_original])
fred_data[, es_replication] <- fred_data[, es_replication] * orig_direction
@@ -421,16 +421,16 @@ align_effect_direction <- function(fred_data, es_original = "es_original", es_re
#'
#' @noRd
#' @examples
-#' fred_data <- data.frame(es_original = c(0.3, 0.5), es_replication = c(0.4, 0.6),
-#' n_original = c(30, 40), n_replication = c(50, 60))
+#' fred_data <- data.frame(es_o = c(0.3, 0.5), es_r = c(0.4, 0.6),
+#' n_o = c(30, 40), n_r = c(50, 60))
#' add_uncertainty(fred_data)
-add_uncertainty <- function(fred_data, es_value_columns = c("es_original", "es_replication"),
- N_columns = c("n_original", "n_replication"),
- vi_columns = c("vi_original", "vi_replication"),
- ci_lower_columns = c("ci.lower_original", "ci.lower_replication"),
- ci_upper_columns = c("ci.upper_original", "ci.upper_replication"),
- p_values = c("p_value_original", "p_value_replication")) {
+add_uncertainty <- function(fred_data, es_value_columns = c("es_o", "es_r"),
+ N_columns = c("n_o", "n_r"),
+ vi_columns = c("vi_o", "vi_r"),
+ ci_lower_columns = c("ci.lower_o", "ci.lower_r"),
+ ci_upper_columns = c("ci.upper_o", "ci.upper_r"),
+ p_values = c("p_value_o", "p_value_r")) {
if (!all.equal(length(es_value_columns), length(N_columns), length(vi_columns), length(ci_lower_columns), length(ci_upper_columns))) {
stop("Length of all column character vectors must be equal")
}
@@ -476,12 +476,12 @@ add_uncertainty <- function(fred_data, es_value_columns = c("es_original", "es_r
#' @importFrom dplyr mutate case_when
code_replication_outcomes <- function(fred_data,
- es_original = "es_original",
- p_original = "p_value_original",
- p_replication = "p_value_replication",
- ci_lower_replication = "ci.lower_replication",
- ci_upper_replication = "ci.upper_replication",
- es_replication = "es_replication") {
+ es_original = "es_o",
+ p_original = "p_value_o",
+ p_replication = "p_value_r",
+ ci_lower_replication = "ci.lower_r",
+ ci_upper_replication = "ci.upper_r",
+ es_replication = "es_r") {
# Convert column names to symbols for dplyr evaluation
es_original_sym <- dplyr::sym(es_original)
@@ -529,7 +529,7 @@ code_replication_outcomes <- function(fred_data,
#' @param power_column Character. Name of target column for power.
#' @return Augmented FReD dataset with power column.
-add_replication_power <- function(fred_data, es_original = "es_original", N_replication = "n_replication", power_column = "power_r") {
+add_replication_power <- function(fred_data, es_original = "es_o", N_replication = "n_r", power_column = "power_r") {
# NA where N_replication is missing
fred_data[, power_column] <- NA
# Return 0 where sample_replication < 4, as pwr.r.test does not work for n < 4
@@ -587,8 +587,8 @@ p_from_r <- function(r, N) {
#' @param vi_columns Character vector of target columns for sampling variances
#' @return FReD dataset with additional columns for sampling variances (metafor's `vi`)
-add_sampling_variances <- function(fred_data, es_value_columns = c("es_original", "es_replication"),
- N_columns = c("n_original", "n_replication"), vi_columns = c("vi_original", "vi_replication")) {
+add_sampling_variances <- function(fred_data, es_value_columns = c("es_o", "es_r"),
+ N_columns = c("n_o", "n_r"), vi_columns = c("vi_o", "vi_r")) {
if (!all.equal(length(es_value_columns), length(N_columns))) {
stop("Length of es_value_columns, N_columns and vi_columns must be equal")
}
@@ -619,19 +619,19 @@ add_sampling_variances <- function(fred_data, es_value_columns = c("es_original"
augment_for_zcurve <- function(fred_data) {
# Ensure fred_data has required columns
- if (!all(c("es_original", "n_original") %in% names(fred_data))) {
- stop("fred_data must contain es_original and n_original columns")
+ if (!all(c("es_o", "n_o") %in% names(fred_data))) {
+ stop("fred_data must contain es_o and n_o columns")
}
# Initialize se and z as NA
fred_data$se <- fred_data$z <- NA
- valid_indices <- !(is.na(fred_data$es_original) | is.na(fred_data$n_original) | fred_data$n_original <= 3)
+ valid_indices <- !(is.na(fred_data$es_o) | is.na(fred_data$n_o) | fred_data$n_o <= 3)
if (any(valid_indices)) {
# Fisher's z transformation
- z <- 0.5 * (log(1 + fred_data$es_original[valid_indices]) - log(1 - fred_data$es_original[valid_indices]))
- fred_data$se[valid_indices] <- 1 / sqrt(fred_data$n_original[valid_indices] - 3)
+ z <- 0.5 * (log(1 + fred_data$es_o[valid_indices]) - log(1 - fred_data$es_o[valid_indices]))
+ fred_data$se[valid_indices] <- 1 / sqrt(fred_data$n_o[valid_indices] - 3)
fred_data$z[valid_indices] <- z / fred_data$se[valid_indices]
}
diff --git a/R/run_apps.R b/R/run_apps.R
index c24c639..1505b35 100644
--- a/R/run_apps.R
+++ b/R/run_apps.R
@@ -143,18 +143,22 @@ run_explorer <- function(offer_install = interactive(), in_background = NULL, au
#' Run the Replication Annotator
#'
-#' Running this function will launch the FReD Replication Annotator shiny app
+#' @description
+#' `r lifecycle::badge("deprecated")`
#'
-#' @return Replication Annotator shiny app
-#' @inheritParams run_app
+#' This function previously launched a local Shiny app but now opens the
+#' hosted web version of the FReD Annotator.
+#'
+#' @return Opens the web annotator in the default browser (invisibly returns NULL).
#' @export
#' @examples
#' if (interactive()) {
-#' # To run the Replication Annotator app:
#' run_annotator()
#' }
-run_annotator <- function(offer_install = interactive(), in_background = NULL, auto_close = interactive(), port = 3839, timeout = 30) {
- run_app(offer_install = offer_install, app = "fred_annotator", in_background = in_background, auto_close = auto_close, port = port, timeout = timeout)
+run_annotator <- function() {
+ .Deprecated(msg = "The local annotator app has been deprecated. Opening the web version.")
+ utils::browseURL("http://forrt.org/apps/fred_annotator.html")
+ invisible(NULL)
}
#' Get the date of last modification
diff --git a/R/zzz_parameters.R b/R/zzz_parameters.R
index a4a5051..f9993f4 100644
--- a/R/zzz_parameters.R
+++ b/R/zzz_parameters.R
@@ -9,7 +9,7 @@
#' - `FRED_DATA_FILE`: The path to the .xlsx file, if you have downloaded it already (or want it to be saved to a particular location). If the file exists, it will be used - otherwise, the file will be downloaded and saved there.
#' - `RETRACTIONWATCH_DATA_FILE`: The path to the RetractionWatch database, if you have downloaded it already (or want it to be saved to a particular location). If the file exists, it will be used - otherwise, the file will be downloaded and saved there.
#' - `RETRACTIONWATCH_URL`: The URL to download the RetractionWatch database. Needs to return the .csv file.
-#' - `FRED_OFFLINE`: Should FReD work offline (TRUE) or online (FALSE). If TRUE, FReD will not download the latest data every time it is loaded. Defaults to FALSE.
+#' - `FRED_OFFLINE`: Should FReD work offline (TRUE) or online (FALSE). If TRUE, FReD will not download the latest data every time it is loaded. Defaults to TRUE.
#' - `FRED_SUPPRESS_STARTUP_MENU`: Should the interactive menu checking for data updates be suppressed (TRUE) or shown (FALSE). If TRUE, all startup messages and interactive prompts related to offline data updates will be suppressed. Defaults to FALSE.
#'
#' @examples
@@ -22,7 +22,7 @@ NULL
.onLoad <- function(libname, pkgname) {
parameters <- list(
- "FRED_DATA_URL" = "https://osf.io/z5u9b/download",
+ "FRED_DATA_URL" = "https://osf.io/2tbvd/download",
"FRED_DATA_FILE" = tempfile(fileext = ".xlsx"),
"RETRACTIONWATCH_DATA_FILE" = tempfile(fileext = ".csv"),
"RETRACTIONWATCH_URL" = "https://api.labs.crossref.org/data/retractionwatch?lukas.wallrich@gmail.com",
@@ -44,7 +44,7 @@ NULL
.onAttach <- function(libname, pkgname) {
- is_offline <- isTRUE(as.logical(Sys.getenv("FRED_OFFLINE", "FALSE")))
+ is_offline <- isTRUE(as.logical(Sys.getenv("FRED_OFFLINE")))
suppress_menu <- isTRUE(as.logical(Sys.getenv("FRED_SUPPRESS_STARTUP_MENU", "FALSE")))
if (is_offline && interactive() && !suppress_menu) {
diff --git a/README.md b/README.md
index 61595de..5fdd694 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,8 @@ experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](h
The goal of the FReD package is to provide various interfaces to the
-FORRT Replication Database. The package includes two shiny apps - the
-FReD Explorer and the FReD Annotator - as well as functions to directly
-access and analyse the dataset.
+FORRT Replication Database. The package includes a shiny app - the
+FReD Explorer - as well as functions to directly access and analyse the dataset.
## FReD Explorer
@@ -25,8 +24,7 @@ The FReD Annotator allows users to annotate lists of articles (e.g. reading list
for classes, or reference lists for draft articles) with any replication findings.
This is intended to make it easier to use replications in teaching and research,
and to thus ensure that claims are based on the best available evidence.
-You can access the FReD Annotator [online](https://forrt-replications.shinyapps.io/fred_annotator/) or by calling `run_annotator()` in your
-R console after you have loaded the package.
+You can access the FReD Annotator [online](https://forrt-replications.shinyapps.io/fred_annotator/).
## Installation
@@ -41,7 +39,6 @@ devtools::install_github("forrtproject/FReD")
``` r
library(FReD)
run_explorer()
-run_annotator()
```
## Accessing the data
diff --git a/inst/NEWS.md b/inst/NEWS.md
index d06ccca..b16c58f 100644
--- a/inst/NEWS.md
+++ b/inst/NEWS.md
@@ -1,5 +1,15 @@
This version history contains noteworthy changes. For a full history of changes, see the [commit history](https://github.com/forrtproject/FReD/commits/main/)
+# FReD 0.2.0
+
+## Breaking Changes
+- **New data source URL**: The package now uses a new data source with updated variable naming conventions
+- **Variable naming convention**: All variable names now use `_o` suffix for original study variables and `_r` suffix for replication study variables (e.g., `es_original` → `es_o`, `n_replication` → `n_r`, `ref_original` → `ref_o`, `doi_replication` → `doi_r`)
+- **`run_annotator()` deprecated**: The local annotator app has been removed. `run_annotator()` now opens the web version at forrt.org instead
+
+## Notes
+- If you have code that references old variable names, you will need to update it to use the new `_o`/`_r` suffixes
+
# FReD 0.1.0
## New features
diff --git a/inst/extdata/snapshot/citation.RDS b/inst/extdata/snapshot/citation.RDS
index ac2f377..b720a63 100644
Binary files a/inst/extdata/snapshot/citation.RDS and b/inst/extdata/snapshot/citation.RDS differ
diff --git a/inst/extdata/snapshot/data.RDS b/inst/extdata/snapshot/data.RDS
index 4dcabe5..2b8adfe 100644
Binary files a/inst/extdata/snapshot/data.RDS and b/inst/extdata/snapshot/data.RDS differ
diff --git a/inst/extdata/snapshot/data_changelog.RDS b/inst/extdata/snapshot/data_changelog.RDS
index dc857e8..f31f3ee 100644
Binary files a/inst/extdata/snapshot/data_changelog.RDS and b/inst/extdata/snapshot/data_changelog.RDS differ
diff --git a/inst/extdata/snapshot/data_description.RDS b/inst/extdata/snapshot/data_description.RDS
index 8127022..cf1282f 100644
Binary files a/inst/extdata/snapshot/data_description.RDS and b/inst/extdata/snapshot/data_description.RDS differ
diff --git a/inst/fred_annotator/changelog.md b/inst/fred_annotator/changelog.md
deleted file mode 100644
index 1004868..0000000
--- a/inst/fred_annotator/changelog.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Changelog
-
-## [0.0.1] - 2021-01-01
-
-* Alpha version - basic interface & first annotations
diff --git a/inst/fred_annotator/global.R b/inst/fred_annotator/global.R
deleted file mode 100644
index 899b3dc..0000000
--- a/inst/fred_annotator/global.R
+++ /dev/null
@@ -1,74 +0,0 @@
-library(shiny)
-library(shinyjs)
-library(bslib)
-library(dplyr)
-library(ggplot2)
-library(DT)
-
-if (FALSE) library(FReD)
-
-options(shiny.maxRequestSize = 20 * 1024^2)
-
-if (!exists("create_citation")) {
- message("Attaching FReD namespace.")
- attach(getNamespace("FReD")) # To enable use of un-exported functions
-}
-
-if (!exists("create_citation")) stop("Failed to attach FReD namespace.")
-
-df <- load_fred_data()
-
-df_display <- df[, c("description", "es_original", "es_replication", "n_original", "n_replication", "osf_link", "contributors", "result", "result2", "ref_original", "ref_replication")]
-df_display$es_original <- round(df_display$es_original, 3)
-df_display$es_replication <- round(df_display$es_replication, 3)
-
-
-dataset_variables <- load_variable_descriptions()
-
-df$ref_original <- gsub("(.{70,}?)\\s", "\\1\n", df$ref_original) # line breaks
-
-# WEBSITE TEXT --------------------------------------------------------------
-
-source("website_text.R", local = TRUE) # Evaluate in calling environment, otherwise fails on app start
-
-source("replication_outcome_styles.R", local = TRUE) # Evaluate in calling environment, otherwise fails on app start
-
-## Add custom theme (formatting)
-custom_css <- ("
-.tab-pane {
- padding: 0px 20px;
-}
-.tab-pane-narrow {
- width: 100%;
- max-width: 30cm;
-}
- .navbar-default .navbar-brand {color:black;}
- .navbar-default .navbar-brand:hover {color:black;}
- .navbar { background-color:#EAEAEA;}
- .navbar-default .navbar-nav > li > a {color: dark grey;}
- .navbar-default .navbar-nav > .active > a,
- .navbar-default .navbar-nav > .active > a:focus,
- .navbar-default .navbar-nav > .active > a:hover {color:black;background-color:#fc2d2d;}
- .navbar-default .navbar-nav > li > a:hover {color:black;background-color:#A6A6A6;text-decoration}
- .page-like {
- background-color: white;
- padding: 20px;
- margin: 20px auto;
- border-radius: 8px;
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
- max-width: 800px;
- }
-")
-
-custom_theme <- bs_theme(
- version = 5,
- bg = "#FFFFFF",
- fg = "#382f2f",
- primary = "#a62828",
- secondary = "#FF374B",
- base_font = "Calibri"
- )
-
-
-
-
diff --git a/inst/fred_annotator/red.png b/inst/fred_annotator/red.png
deleted file mode 100644
index 40ef090..0000000
Binary files a/inst/fred_annotator/red.png and /dev/null differ
diff --git a/inst/fred_annotator/replication_outcome_styles.R b/inst/fred_annotator/replication_outcome_styles.R
deleted file mode 100644
index 1fe9238..0000000
--- a/inst/fred_annotator/replication_outcome_styles.R
+++ /dev/null
@@ -1,70 +0,0 @@
-success_criteria_colors <- tibble::tribble(
- ~criterion, ~label, ~color,
-
- # significance_r outcome_report labels
- "significance_r", "OS not significant", "#D3D3D3",
- "significance_r", "failure", "#FF7F7F",
- "significance_r", "success", "#8FBC8F",
- "significance_r", "failure (reversal)", "darkred",
-
- # significance_agg outcome_report labels
- "significance_agg", "success", "#8FBC8F",
- "significance_agg", "failure", "#FF7F7F",
- "significance_agg", "failure (reversal)", "darkred",
-
- # consistency_ci outcome_report labels
- "consistency_ci", "success", "#8FBC8F",
- "consistency_ci", "failure", "#FF7F7F",
-
- # consistency_pi outcome_report labels
- "consistency_pi", "success", "#8FBC8F",
- "consistency_pi", "failure", "#FF7F7F",
-
- # homogeneity outcome_report labels
- "homogeneity", "success", "#8FBC8F",
- "homogeneity", "failure", "#FF7F7F",
-
- # homogeneity_significance outcome_report labels
- "homogeneity_significance", "OS not significant", "#D3D3D3",
- "homogeneity_significance", "success (homogeneous and jointly significantly above 0)", "#8FBC8F",
- "homogeneity_significance", "failure (not homogeneous but jointly significantly above 0)", "#efa986",
- "homogeneity_significance", "failure (not homogeneous and not significant)", "darkred",
- "homogeneity_significance", "failure (homogeneous but not significant)", "#FF7F7F",
-
- # small_telescopes outcome_report labels
- "small_telescopes", "success", "#8FBC8F",
- "small_telescopes", "failure", "#FF7F7F"
-)
-
-success_criterion_note <- c(
- significance_r = "Success was based on whether the replication effect was statistically significant and in the same direction as the original. Beware: *p*-values are calculated from raw effect sizes and sample sizes, and may differ from those reported in studies that adjusted for covariates or clustering.",
-
- significance_agg = "Success was based on whether a meta-analytic combination of original and replication effects was statistically significant. Beware: *p*-values are calculated from raw effect sizes and sample sizes, and may differ from those reported in studies that adjusted for covariates.",
-
- consistency_ci = "Success was based on whether the original effect size fell within the confidence interval of the replication. Beware: confidence intervals are based on raw effect sizes and sample sizes, and may differ from those reported in adjusted models.",
-
- consistency_pi = "Success was based on whether the replication effect size fell within the prediction interval from the original study. Beware: prediction intervals are based on raw effect sizes and sample sizes, and may differ from those reported in adjusted models.",
-
- homogeneity = "Success was based on a test of heterogeneity (Q-test) between original and replication effects. Beware: test statistics are based on raw effect sizes and sample sizes, and may differ from those reported in adjusted analyses in the original reports.",
-
- homogeneity_significance = "Success was based on both effect homogeneity and statistical significance. Replications where the effects were homogeneous and jointly significantly different from zero were considered as successes, while those that were either not homogeneous or not significantly different from zero were considered as failures. Beware: all values are based on raw effect sizes and sample sizes, and may differ from those reported in adjusted models.",
-
- small_telescopes = "Success was based on whether the replication effect exceeded the threshold that would give 33% power in the original study. Beware: power was calculated based on a simple test of a correlation, thus not accounting for any specific design features relevant to the study."
-)
-
-success_criterion_o_ns <- c(
- significance_r = TRUE,
-
- significance_agg = TRUE,
-
- consistency_ci = FALSE,
-
- consistency_pi = FALSE,
-
- homogeneity = FALSE,
-
- homogeneity_significance = TRUE,
-
- small_telescopes = FALSE
-)
-
diff --git a/inst/fred_annotator/server.R b/inst/fred_annotator/server.R
deleted file mode 100644
index ff7d781..0000000
--- a/inst/fred_annotator/server.R
+++ /dev/null
@@ -1,759 +0,0 @@
-
-# ANNOTATOR ---------------------------------------------------------------
-
-server <- function(input, output, session) {
- doi_vector <- reactiveValues(dois = c(), selected_rows = NULL)
- retracted_dois <- reactiveVal(NULL)
-
-
- session$onSessionEnded(function() {
- if (Sys.getenv("SHINY_FRED_AUTOCLOSE") == "TRUE") {
- message("App has ended because the session was ended.")
- stopApp()
- }
- })
-
- # Disclaimer --------------------------------------------------------------
-
- showModal(modalDialog(
- title = welcome_title,
- welcome_text,
- size = "l",
- easyClose = TRUE
- ))
-
- reactive_df <- reactiveVal(df)
- selected_refs <- reactiveVal()
-
- observe({
- selected_refs(
- reactive_df() %>%
- filter(doi_original %in% doi_vector$dois)
- )
- })
-
- # Recalculate replication success based on criterion
- assess_success <- function(result, success_criterion) {
- assess_replication_outcome(result$es_original, result$n_original, result$es_replication, result$n_replication,
- criterion = success_criterion)$outcome_report
- }
-
- observeEvent({input$success_criterion; selected_refs()}, {
- if (nrow(selected_refs()) > 0) {
- updated_df <- selected_refs() %>%
- arrange(ref_original) %>%
- filter(if (input$validated == "TRUE") validated == 1 else TRUE) %>%
- mutate(
- result = assess_success(., input$success_criterion) %>% cap_first_letter(),
- result = factor(
- result,
- levels = rev(c(
- "not coded",
- unique(result[grepl("success", result, ignore.case = TRUE)]),
- unique(result[grepl("failure", result, ignore.case = TRUE)]),
- unique(result[!(result %in% c("not coded", "OS not significant")) &
- !grepl("success|failure", result, ignore.case = TRUE)]),
- "OS not significant"
- ))
- )
- )
-
- selected_refs(updated_df)
- }
- }, ignoreNULL = TRUE)
-
-
-
- outcome_colors <- reactive({
- criterion_colors <- success_criteria_colors %>%
- dplyr::filter(criterion == input$success_criterion)
-
- outcome_colors <- setNames(criterion_colors$color, criterion_colors$label %>% cap_first_letter())
- c(outcome_colors, "Not coded" = "#C8C8C8")
- })
-
- observeEvent(input$load_retractions, {
- showModal(modalDialog(
- title = "Loading Retraction Database",
- "Please wait while the retraction database is being loaded.",
- easyClose = FALSE,
- footer = modalButton("Cancel") # so that button does not say "Dismiss"
- ))
- retracted_dois(load_retractionwatch() %>%
- dplyr::filter(RetractionNature == "Retraction") %>%
- dplyr::select(OriginalPaperDOI))
- updated_df <- reactive_df() %>%
- dplyr::left_join(retracted_dois() %>% mutate(retracted_replication = TRUE), retraction_data(),
- by = c("doi_replication" = "OriginalPaperDOI"), na_matches = "never") %>%
- dplyr::left_join(retracted_dois() %>% mutate(retracted_original = TRUE), retraction_data(),
- by = c("doi_original" = "OriginalPaperDOI"), na_matches = "never") %>%
- dplyr::mutate(retracted_replication = dplyr::coalesce(retracted_replication, FALSE),
- retracted_original = dplyr::coalesce(retracted_original, FALSE),
- ref_original = ifelse(
- retracted_original & !grepl("^RETRACTED", ref_original, ignore.case = TRUE),
- paste("RETRACTED:", ref_original),
- ref_original
- ),
- ref_replication = ifelse(
- retracted_replication & !grepl("^RETRACTED", ref_replication, ignore.case = TRUE),
- paste("RETRACTED:", ref_replication),
- ref_replication
- ))
- reactive_df(updated_df)
- removeModal()
- showNotification(shiny::HTML(paste("Retraction database loaded successfully.
FReD contains ", sum(updated_df$retracted_original),
- " retracted original studies and ", sum(updated_df$retracted_replication), " retracted replication studies.")))
- shinyjs::disable("load_retractions")
- })
-
- reactive_distinct_fred_entries <- reactive({
- df <- reactive_df() %>%
- arrange(desc(validated == 1)) %>%
- filter(if (input$validated == "TRUE") validated == 1 else TRUE) %>%
- select(doi_original, ref_original)
-
- dplyr::bind_rows(
- df %>% dplyr::filter(!is.na(doi_original)) %>% dplyr::group_by(doi_original) %>% dplyr::slice(1) %>% dplyr::ungroup(),
- df %>% dplyr::filter(is.na(doi_original))
- ) %>%
- distinct()
- })
-
- output$button_area <- renderUI({
- if (length(doi_vector$dois) == 0) {
-
- actionButton("process_button", "Process References")
-
- } else {
- tagList(
-
- actionButton("add_button", "Add references"),
-
- br(), HTML(" "), br(),
-
- actionButton("replace_button", "Replace references")
- )
- }
- })
-
- parse_dois <- function() {
-
- # Check if both text box and file are provided
- if (nchar(input$references) > 0 && is.null(input$upload) == FALSE) {
- showNotification("Both text box and file will be processed.")
- }
-
- dois <- c()
-
- # Process text box input
- if (nchar(input$references) > 0) {
- text_dois <- extract_dois_from_text(input$references, report = TRUE)
- dois <- c(dois, text_dois)
- }
-
- # Process uploaded file
- if (is.null(input$upload) == FALSE) {
- file_dois <- extract_dois_from_file(input$upload$datapath)
- dois <- c(dois, file_dois)
- }
-
- if (input$navbar == "Introduction") {
- nav_select("navbar", "Study Selection")
- }
-
- out <- unique(dois)
-
- if (length(out) == 0) {
- showModal(modalDialog(
- title = "No DOIs found",
- "Please check the format of the input",
- easyClose = TRUE
- ))
- }
-
- out
-
- }
-
-
- observe({
- if (input$navbar == "Report") {
- if (!length(doi_vector$dois > 0)) {
- showModal(modalDialog(
- title = "No DOIs selected",
- "Please select DOIs before proceeding to the report.",
- easyClose = TRUE
- ))
- nav_select("navbar", "Study Selection")
- }
- }
- })
-
-
- observeEvent(input$process_button, {
- doi_vector$dois <- parse_dois()
- })
-
- observeEvent(input$add_button, {
- doi_vector$dois <- union(doi_vector$dois, parse_dois())
- })
-
- observeEvent(input$replace_button, {
- doi_vector$dois <- parse_dois()
- })
-
-
- visible <- reactiveVal(FALSE)
-
- output$doi_display <- renderText({
- paste(doi_vector$dois, collapse = "\n")
- })
-
- output$collapsible_dois <- renderUI({
- if (length(doi_vector$dois) > 0) {
- tags$div(
- id = "doi_display_container",
- style = if (visible()) "display: block;" else "display: none;",
- verbatimTextOutput("doi_display"),
- actionButton("copy_btn", "Copy DOIs", onclick = "copyText()")
- )
- }
- })
-
- observeEvent(input$hidden_toggle_btn, {
- visible(!visible())
- })
-
- output$showToggle <- reactive({
- length(doi_vector$dois) > 0
- })
-
- # Need to be active in background to allow toggling of DOI display
- outputOptions(output, "showToggle", suspendWhenHidden = FALSE)
- outputOptions(output, "doi_display", suspendWhenHidden = FALSE)
-
-
- extract_dois_from_text <- function(text, report = FALSE) {
-
- pattern <- "10(?:\\.[0-9]{4,})?\\/[^\\s]*[^\\s\\.,]" # based on pattern used by Zotero Chrome plugin
-
- # Find DOIs in each element; returns a list (each element: DOIs found in input string)
- matched <- regmatches(text, gregexpr(pattern, text, perl = TRUE))
- dois <- unlist(matched)
- dois <- sub("\\s*([,;.])*$", "", dois) # remove trailing punctuation
-
-
- # Keep only the actual DOI (removing prefix)
- dois_clean <- sub("(?i)(doi:(?:\\/\\/)?\\s*|https?://doi\\.org/)\\s*", "", dois, perl=TRUE)
- dois_clean
-
- # If a DOI ends with a closing parenthesis and there are more ')' than '(', remove trailing ')'
- dois <- sapply(dois, function(x) {
- if (substr(x, nchar(x), nchar(x)) == ")" && sum(strsplit(x, "")[[1]] == "(") < sum(strsplit(x, "")[[1]] == ")")) {
- substr(x, 1, nchar(x) - 1)
- } else {
- x
- }
- })
-
-
- # deduplicate
- dois <- unique(unname(dois))
-
- if (report) {
- # Count number of non-blank lines
- n_lines <- sum(nzchar(trimws(unlist(strsplit(text, "\n")))))
-
- showNotification(paste("Found and added", length(dois), ifelse(length(dois) == 1, "DOI", " distinct DOIs"),
- "from", n_lines, ifelse(n_lines == 1, "line", "lines"), "of text input."),
- type = "message", duration = 10)
-
- }
-
- dois
-
- }
-
-
- extract_dois_from_file <- function(file_path) {
- dois <- c()
- if (file_path != "") {
- if (grepl("\\.pdf$", file_path)) {
- # Extract DOIs from PDF file
- pdf_text <- pdftools::pdf_text(file_path)
- dois <- extract_dois_from_text(pdf_text)
- } else if (grepl("\\.bib$", file_path)) {
- # Extract DOIs from BibTeX file
- bib_text <- readLines(file_path)
- dois <- extract_dois_from_text(bib_text)
- }
- }
-
- dois <- unique(unname(dois))
-
- showNotification(paste("Found and added", length(dois), ifelse(length(dois) == 1, "DOI", " distinct DOIs"),
- "from your file."),
- type = "message", duration = 10)
-
- dois
-
- }
-
-
- # Initial rendering of the DataTable
- output$selected_references <- renderDT({
- data <- datatable(
- data.frame(doi_original = character(0), doi_replication = character(0),
- description = character(0), es_original = numeric(0),
- es_replication = numeric(0), result = character(0)),
- escape = FALSE, options = list(pageLength = 10), filter = "none"
- )
- formatRound(data, columns = c('es_original', 'es_replication'), digits = 2)
-
- }, server = TRUE)
-
- # Observer to update data reactively
- observe({
- proxy <- dataTableProxy('selected_references', deferUntilFlush = FALSE)
-
- if (length(doi_vector$dois) == 0) {
- replaceData(proxy, data.frame(doi_original = character(0), doi_replication = character(0),
- description = character(0), es_original = numeric(0),
- es_replication = numeric(0), result = character(0)), resetPaging = TRUE)
- return()
- }
-
- if (length(doi_vector$dois) > 0) {
-
- data <- data.frame(doi_original = doi_vector$dois) %>%
- left_join(reactive_df(), by = c("doi_original")) %>%
- mutate(
- in_FReD = doi_original %in% reactive_df()$doi_original,
- doi_original = purrr::map_chr(doi_original, ~sprintf('%s', .x, .x)),
- doi_replication = purrr::map_chr(doi_replication, ~if (!is.na(.x)) sprintf('%s', .x, .x) else NA_character_)
- ) %>%
- arrange(!in_FReD, is.na(doi_replication)) %>%
- select(doi_original, doi_replication, description, es_original, es_replication, result)
-
- replaceData(proxy, data, resetPaging = TRUE)
- updateFilters(proxy, data)
- }
-
- })
-
-
- output$database_search <- renderDT({
- reactive_distinct_fred_entries() %>%
- datatable(selection = 'multiple', options = list(pageLength = 10))
- }, server = TRUE)
-
- observe({
- selected_rows <- which(reactive_distinct_fred_entries()$doi_original %in% doi_vector$dois)
- proxy <- dataTableProxy('database_search')
- selectRows(proxy, selected_rows)
- doi_vector$selected_rows <- selected_rows
- })
-
- # Observe changes in selected rows - with debounce to avoid loops
- debounced_rows_selected <- debounce(reactive(input$database_search_rows_selected), 250)
-
- observeEvent(debounced_rows_selected(), ignoreNULL = FALSE, {
- current_rows <- input$database_search_rows_selected
-
- # Determine newly selected and deselected rows
- newly_selected_rows <- setdiff(current_rows, doi_vector$selected_rows)
- deselected_rows <- setdiff(doi_vector$selected_rows, current_rows)
-
- # Handle deselection
- if (length(deselected_rows) > 0) {
- remove_dois <- reactive_distinct_fred_entries()$doi_original[deselected_rows]
- duplicate_rows <- which(reactive_distinct_fred_entries()$doi_original %in% remove_dois)
- current_rows <- setdiff(current_rows, duplicate_rows)
- doi_vector$dois <- setdiff(doi_vector$dois, remove_dois)
- }
-
- # Handle new selections
- if (length(newly_selected_rows) > 0) {
- add_dois <- reactive_distinct_fred_entries()$doi_original[newly_selected_rows]
- duplicate_rows <- which(reactive_distinct_fred_entries()$doi_original %in% add_dois)
- current_rows <- union(current_rows, duplicate_rows)
- doi_vector$dois <- union(doi_vector$dois, add_dois)
- }
- doi_vector$selected_rows <- current_rows
-
- # Re-select rows in the DT to reflect any changes
- proxy <- dataTableProxy('database_search')
- selectRows(proxy, current_rows)
- })
-
-
- # Need to be active in background to stay synced with DOI list
- outputOptions(output, "selected_references", suspendWhenHidden = FALSE)
- outputOptions(output, "database_search", suspendWhenHidden = FALSE)
-
- output$references_barplot <- plotly::renderPlotly({
-
- df <- tibble(doi_original = doi_vector$dois, in_FReD = doi_original %in% reactive_df()$doi_original) %>%
- mutate(in_FReD = ifelse(in_FReD, "yes", "no"))
-
- # Calculate the counts and proportions
- df_summary <- df %>%
- count(in_FReD) %>%
- mutate(proportion = n / sum(n),
- label = paste(in_FReD, "\n(", scales::percent(proportion, accuracy = 1), ")"))
-
- color_values = c("no" = "#FF7F7F", "yes" = "#8FBC8F")
-
-
-
- # Create the plot
- p <- df_summary %>%
- ggplot(aes(y = "", x = proportion, fill = in_FReD)) +
- geom_bar(stat = "identity", position = "fill") +
- geom_text(aes(label = label), position = position_fill(vjust = 0.5), size = 3) + # Adjust size as needed
- theme_minimal() +
- labs(y = "", x = "Share", title = "Are replications for references in FReD?") +
- scale_x_continuous(labels = scales::percent) +
- scale_fill_manual(values = color_values) +
- guides(fill = "none") + theme_void()
-
- plotly::ggplotly(p, tooltip = NULL, height = 150) %>%
- plotly::layout(xaxis = list(fixedrange = TRUE), yaxis = list(fixedrange = TRUE)) %>%
- plotly::config(displayModeBar = FALSE)
-
- })
-
- output$outcomes_barplot <- plotly::renderPlotly({
-
- df <- selected_refs()
-
- message("Selected refs: ", nrow(df))
-
- if (any(df$retracted_replication)) {
- message("any")
- count_retracted_replications <- sum(df$retracted_replication)
- df <- df %>% dplyr::filter(!retracted_replication)
- } else {
- message("none")
- count_retracted_replications <- 0
- }
-
- message(count_retracted_replications)
-
- validate(
- need(nrow(df) > 0, "", label = "No replications found")
- )
-
- retraction_note <- ifelse(
- count_retracted_replications > 0,
- paste0(
- "Note: ", count_retracted_replications,
- " retracted replication stud",
- ifelse(count_retracted_replications == 1, "y was", "ies were"),
- " excluded from the plot."
- ),
- ""
- )
-
- p <- df %>%
- mutate(result = result %>% forcats::fct_na_value_to_level("not coded")) %>%
- ggplot(aes(y = result, fill = result)) +
- geom_bar() +
- theme_minimal() +
- labs(y = "", x = "Count", title = "Outcomes of replication attempts", caption = retraction_note, fill = "") +
- scale_fill_manual(values = outcome_colors())
-
- plotly::ggplotly(p, tooltip = NULL) %>%
- plotly::config(displayModeBar = FALSE) %>%
- plotly::layout(xaxis = list(fixedrange = TRUE), yaxis = list(fixedrange = TRUE))
-
- })
-
- # Functions to generate markdown
-
-
- assess_outcome <- function(replications, ..., success_criterion = c("consistency", "significance_r"), return_html = TRUE) {
-
- ref_original <- replications$ref_original[1]
-
- replications %>%
- mutate(assess_replication_outcome(.data$es_original, .data$n_original, .data$es_replication,
- .data$n_replication, criterion = success_criterion)) %>%
- # Remove uncoded duplicates
- arrange(desc(validated == 1), is.na(outcome_report)) %>%
- filter(!(!is.na(doi_replication) & duplicated(doi_replication) & is.na(outcome_report))) %>%
- filter(!(duplicated(ref_replication) & is.na(outcome_report))) %>%
- group_by(ref_replication) %>%
- summarise(
- outcome_report = if(length(unique(outcome_report)) == 1) unique(outcome_report) else paste(outcome_report, collapse = ", "),
- outcome = case_when(
- all(is.na(outcome)) ~ "not coded",
- all(outcome == "success") ~ " success",
- all(outcome == "failure") ~ "failure",
- all(outcome == "OS not significant") ~ "OS not significant",
- .default = "mixed"),
- ref_original = first(ref_original)
- ) %>%
- ungroup() %>%
- summarise(
- replications = paste0(" - **", cap_first_letter(dplyr::coalesce(outcome_report, "not coded")), ":** ", ref_replication, collapse = "\n"),
- overall_outcome = case_when(
- all(outcome == "not coded") ~ if (return_html) "✏" else "[NC]",
- all(outcome == "success") ~ if (return_html) "✔" else "[Re]",
- all(outcome == "failure") ~ if (return_html) "✖" else "[¬Re]",
- all(outcome == "OS not significant") ~ if (return_html) "❔" else "[N/A]", # Using a dash for N/A
- .default = if (return_html) "❓" else "[?Re]"
- )
- ) %>%
- mutate(ref_original = ref_original)
- }
-
-
- generate_markdown <- function(df_filtered, ...) {
- # Call the new legend function
- legend <- generate_legend_markdown(input$success_criterion)
-
- extra_args <- list(...)
- extra_args["success_criterion"] <- input$success_criterion
-
- # The main body of the report is generated here
- report_body <- if (nrow(df_filtered) > 0) {
- df_filtered %>%
- mutate(
- ref_original = stringr::str_replace_all(ref_original, "\n", " "),
- ref_original = ifelse(stringr::str_detect(ref_original, stringr::fixed("doi.org")),
- ref_original,
- paste0(ref_original, " [https://doi.org/", doi_original, "](https://doi.org/", doi_original, ")")),
- doi_urls = ifelse(!is.na(doi_replication),
- paste0("https://doi.org/", doi_replication),
- ifelse(!is.na(osf_link), osf_link, "")),
- ref_replication = stringr::str_replace_all(ref_replication, "\n", " "),
- ref_replication = ifelse(stringr::str_detect(ref_replication, stringr::fixed("doi.org")) | doi_urls == "",
- ref_replication,
- paste0(ref_replication, " [", doi_urls, "](", doi_urls, ")")),
- id_original = coalesce(doi_original, ref_original)
- ) %>%
- group_by(id_original) %>%
- group_modify(~do.call(assess_outcome, c(list(.x), extra_args))) %>%
- ungroup() %>%
- mutate(
- markdown = paste0(
- "##### ", overall_outcome, " ", ref_original, "\n\n",
- replications, "\n\n"
- )
- ) %>%
- pull(markdown) %>%
- paste(collapse = "\n")
- } else {
- "" # Return empty string if no data
- }
-
- # Combine the legend and the report body
- final_report <- paste0(
- legend,
- "\n## Replication Outcomes\n\n",
- report_body
- )
-
- return(final_report)
- }
-
-
- #' Generate Markdown for the Report Legend
- #'
- #' Creates a markdown table explaining the symbols used in the report,
- #' dynamically including the note for the selected success criterion.
- #'
- #' @param success_criterion The currently selected success criterion from the UI.
- #' @return A character string containing the markdown for the legend.
- generate_legend_markdown <- function(success_criterion) {
- criterion_explanation <- success_criterion_note[success_criterion]
-
-
-
- legend_md <- glue::glue(
- "**Note:** {criterion_explanation}\n\n",
- "### Legend\n\n",
-
- "| | |\n",
-
- "|:------:|:----------------------------------------------------------|\n",
-
- "| ✔ | *Success*: All replications of the original study were successful. |\n",
- "| ✖ | *Failure*: All replications of the original study failed. |\n",
- "| ❓ | *Mixed Results*: The replications of the original study had mixed outcomes (e.g., some succeeded, some failed). |\n",
-
- if (success_criterion_o_ns[success_criterion]) {
- "| ❔ | *Original Not Significant*: The original study's p-value was >= .05, so 'success' is not applicable. |\n"
- } else {
- ""
- },
-
- "| ✏ | *Not Coded*: The outcome of the replication(s) has not yet been coded. |"
-
- )
- return(legend_md)
- }
-
- markdown_output <- reactive({
- generate_markdown(selected_refs())
- })
-
- output$refs_annotated <- renderUI({
- # Validate that there are selected references
- validate(
- need(nrow(selected_refs()) > 0, "", label = "No replications found")
- )
-
- # Render the markdown from the reactive expression
- tags$div(
- class = "page-like",
- style = "white-space: pre-wrap; overflow-y: auto; max-height: 800px;",
- HTML(markdown::markdownToHTML(text = paste("# Replication Report\n", markdown_output()), fragment.only = TRUE))
- )
- })
-
-
-
- # observe({
- # df <- reactive_df() %>%
- # filter(doi_original %in% doi_vector$dois)
- #
- # if (nrow(df) == 0) {
- # shinyjs::disable("downloadPdf")
- # } else {
- # shinyjs::enable("downloadPdf")
- # }
- # })
- #
- # output$downloadPdf <- downloadHandler(
- # filename = function() {
- # paste("reading_list", Sys.Date(), ".pdf", sep = "")
- # },
- # content = function(file) {
- # tempReport <- tempfile(fileext = ".Rmd")
- #
- # df <- reactive_df() %>%
- # filter(doi_original %in% doi_vector$dois)
- #
- # markdown_output <- generate_markdown(df, return_html = FALSE)
- #
- # # Create the Rmd content
- # rmd_content <- paste0(
- # "---\n",
- # "title: \"Annotated Reading List\"\n",
- # "output:\n pdf_document:\n latex_engine: xelatex\n",
- # "---\n\n",
- # markdown_output
- # )
- #
- # writeLines(rmd_content, con = tempReport)
- #
- # out <- rmarkdown::render(tempReport, quiet = TRUE)
- # file.rename(out, file)
- # }
- # )
-
- output$downloadWord <- downloadHandler(
- filename = function() {
- paste("reading_list", Sys.Date(), ".docx", sep = "")
- },
- content = function(file) {
- tempReport <- tempfile(fileext = ".Rmd")
-
- # Create the Rmd content
- rmd_content <- paste0(
- "---\n",
- "title: \"Annotated Reading List\"\n",
- "output:\n word_document:\n reference_docx: null\n",
- "---\n\n",
- "*NB: This contains bookmarks that are likely displayed as []. You can change your Word settings to hide those; we are still working to remove them when creating the file.* \n\n",
- markdown_output() %>% stringr::str_remove(".*\n")
- )
-
-
- writeLines(rmd_content, con = tempReport)
-
- out <- rmarkdown::render(tempReport, quiet = TRUE)
-
- # # Pandoc inserts unnecessary bookmarks that are displayed and look like errors - so remove them
- # remove_bookmarks <- function(doc_path, output_path) {
- # doc <- officer::read_docx(doc_path)
- #
- # # Get all bookmarks in the document
- # bookmarks <- officer::docx_bookmarks(doc)
- #
- # # Iterate over all bookmarks and remove them
- # for (bookmark in bookmarks) {
- # cursor <- officer::cursor_bookmark(doc, bookmark)
- # doc <- officer::cursor_reach(cursor)
- # doc <- officer::body_remove(cursor)
- # }
- #
- # # Save the modified document to the output path
- # print(doc, target = output_path)
- # }
- #
- # remove_bookmarks(out, out)
-
- file.rename(out, file)
- }
- )
-
-
- output$replicability_plot <- plotly::renderPlotly({
-
- df <- selected_refs()
-
- validate(
- need(nrow(df) > 0, "", label = "No replications found")
- )
-
- df$significant_original <- c("Not significant", "Significant")[(df$p_value_original < .05) + 1] %>%
- factor(levels = c("Not significant", "Significant"))
- df$significant_replication <- c("Not significant", "Significant")[(df$p_value_replication < .05) + 1] %>%
- factor(levels = c("Not significant", "Significant"))
-
-
- df$scatterplotdescription <- paste(stringr::str_wrap(df$description, 50), "\nr(original) = ",
- round(df$es_original, 3),
- ", r(replication) = ",
- round(df$es_replication, 3),
- sep = ""
- )
-
- pointsize <- ifelse(nrow(df) < 10, 5, ifelse(nrow(df) < 100, 4, 3))
-
- df <- df %>% filter(!is.na(es_replication))
-
- scatterplot <-
- ggplot(df, aes(x = es_original, y = es_replication, text = scatterplotdescription)) +
- geom_hline(aes(yintercept = 0), linetype = 2) +
- geom_abline(intercept = 0, slope = 1, color = "Grey60") +
- geom_point(aes(fill = result), size = pointsize, color = "Grey30", shape = 21, alpha = .8) +
- # geom_point(data = df_temp[s3, ], fill = "#0077d9", color = "#f2ef1b", shape = 4) +
- geom_rug(data = df[df$significant_original == "Significant", ],
- color = "#4DCCD0", linewidth = 1, sides = "b", alpha = .6) +
- geom_rug(data = df[df$significant_original == "Not significant", ],
- color = "#FA948C", linewidth = 1, sides = "b", alpha = .6) +
- geom_rug(data = df[df$significant_replication == "Significant", ],
- color = "#4DCCD0", linewidth = 1, sides = "l", alpha = .6) +
- geom_rug(data = df[df$significant_replication == "Not significant", ],
- color = "#FA948C", linewidth = 1, sides = "l", alpha = .6) +
- scale_x_continuous(name = "Original Effect Size", limits = c(0, 1), breaks = c(0, .25, .5, .75, 1)) +
- scale_y_continuous(name = "Replication Effect Size", limits = c(-.5, 1), breaks = c(-.5, -.25, 0, .25, .5, .75, 1)) +
- # ggtitle("") + #xlab("") + ylab("") +
- # scale_size_continuous(name="Power",range=c(.5,3.5)) +
- #scale_color_discrete(guide = "none") +
- scale_fill_manual(values = outcome_colors(), drop = FALSE) +
- theme_bw() +
- labs(fill = "Replication Outcome", color = "Significance")
- #theme(legend.position = "inside", plot.margin = unit(c(-2,-1.5,2,2), "lines"))
- #theme(legend.position = "none")
-
- plotly::ggplotly(scatterplot, tooltip = "text") %>%
- plotly::config(displayModeBar = FALSE) %>%
- plotly::layout(xaxis = list(fixedrange = TRUE), yaxis = list(fixedrange = TRUE))
- })
-
-}
diff --git a/inst/fred_annotator/ui.R b/inst/fred_annotator/ui.R
deleted file mode 100644
index 308ef2c..0000000
--- a/inst/fred_annotator/ui.R
+++ /dev/null
@@ -1,149 +0,0 @@
-# ANNOTATOR ---------------------------------------------------------------
-
-# UI definition
-
-sidebar_contents <- sidebar(
- id = "mySidebar",
- padding = 10,
- width = 300,
- textAreaInput("references", "Paste references with DOIs", placeholder = "Paste references here...", width = '95%', height = '200px'),
- fileInput("upload", HTML('Or upload reference list (PDF, citation or text file). Must contain DOIs; otherwise process it with Crossref first. Copying references from PDF generally works better than PDF upload. Linebreaks within DOIs prevent DOI recognition - preprocessing via an LLM is recommended.'), accept = c("application/pdf", " text/plain", ".bib", ".ris")),
- uiOutput("button_area"),
- checkboxInput("validated", "Use validated database entries only", value = TRUE),
- actionButton("load_retractions", "Load Retraction Database", icon = icon("database")),
- hr(),
- radioButtons("success_criterion",
- label = popover(
- trigger = list(
- "Success criterion",
- icon(c("info-circle"))
- ),
- "Check our ",
- a("vignette", href = "https://forrt.org/FReD/articles/success_criteria.html", target = "_blank"),
- "for details on the different success criteria."
- ),
- choices = c("Significance of Replication" = "significance_r",
- "Aggregated Significance" = "significance_agg",
- "Consistency with CI" = "consistency_ci",
- "Consistency with PI" = "consistency_pi",
- "Homogeneity" = "homogeneity",
- "Homogeneity & Significance" = "homogeneity_significance",
- "Small Telescopes" = "small_telescopes"),
- selected = "significance_r"),
- div(
- HTML("NB: The success criteria (e.g., p-values, CIs) are calculated from raw effect and sample sizes. They may differ from original reports that used adjusted models.")
- ),
- conditionalPanel(
- condition = "output.showToggle", # This JavaScript condition reacts to Shiny output
- tags$a(id = "toggle_link", "Show/Hide DOIs >", href = "#", class = "btn btn-link"),
- actionButton("hidden_toggle_btn", "Toggle", style = "display: none;")
- ),
- uiOutput("collapsible_dois"),
- tags$script(HTML("
- $(document).on('click', '#toggle_link', function(event) {
- event.preventDefault();
- $('#doi_display_container').toggle(); // Toggle visibility of the DOI display container
- $('#hidden_toggle_btn').click(); // Trigger the hidden button click
- });
- function copyText() {
- const element = document.getElementById('doi_display');
- navigator.clipboard.writeText(element.textContent);
- alert('Copied to clipboard!');
- }
- "))
- )
-
-
-# Define content for each panel
-introduction_content <- nav_panel(
- "Introduction",
- class = "tab-pane-narrow",
- includeMarkdown("www/introduction.md")
-
-)
-
-study_selection_content <- nav_panel(
- "Study Selection",
- h2("Selected studies"),
- HTML(paste("Please report potential issues with DOI processing.", collapse = "")),
- DTOutput("selected_references", fill = FALSE),
- h2("Available studies in FReD"),
- HTML(paste("Select/unselect rows to add/remove studies from the report. You can search by DOI, reference or description.
- At present, only studies where the DOI is listed in FReD can be used in the annotator, rows without DOIs will
- be ignored here, but can be retrieved from the FReD dataset."), sep = ""), # the FReD dataset
- DTOutput("database_search", fill = FALSE)
- )
-
-
-report_content <- nav_panel(
- "Report",
- div(
- style = "max-width: 1000px; margin: auto;",
- plotly::plotlyOutput("references_barplot", height = 150),
- br(),
- uiOutput("success_note"),
- plotly::plotlyOutput("outcomes_barplot"),
- br(),
- plotly::plotlyOutput("replicability_plot", height = "600px"),
- scatterplot_explanation
- ),
- div(
- style = "max-width: 450px; margin: auto; display: flex; gap: 10px; align-items: center; !important",
- downloadButton("downloadWord", "Download annotated Word file"),
- # downloadButton("downloadPdf", "Download annotated PDF")
- ),
- shinycssloaders::withSpinner(uiOutput("refs_annotated"))
-)
-
-about_content <- nav_panel("About",
- class = "tab-pane-narrow",
-
- markdown(about_page),
- markdown(paste0("#", get_dataset_changelog())),
- h2("Package changelog"),
- includeMarkdown(system.file("NEWS.md", package = "FReD")),
- img(src = "fred.png", height = 80),
- tags$style(HTML("
-
- "))
-)
-
-replicationhub_link <- nav_item(a("FORRT Replication Hub", href = "https://www.forrt.org/replication-hub/", target = "_blank"))
-
-ui <- tagList(
- tags$head(
- tags$style(HTML("
- .navbar-brand {
- display: flex;
- align-items: center;
- margin-left: 15px;
- height: 50px;
- }
- .navbar-brand img {
- margin-right: 10px;
- }
- .navbar-nav > li > a {
- line-height: 50px !important;
- }
- .dark-mode-nav {
- margin-left: auto;
- }
- "))
- ),
- page_navbar(
- theme = custom_theme,
- id = "navbar",
- title = tags$a(
- class = "navbar-brand",
- href = "#",
- tags$img(src = "fred.png", height = "40")
- ),
- sidebar = sidebar_contents,
- introduction_content,
- study_selection_content,
- report_content,
- about_content,
- replicationhub_link,
- nav_item(input_dark_mode(), class = "dark-mode-nav")
- )
-)
diff --git a/inst/fred_annotator/website_text.R b/inst/fred_annotator/website_text.R
deleted file mode 100644
index 6932488..0000000
--- a/inst/fred_annotator/website_text.R
+++ /dev/null
@@ -1,80 +0,0 @@
-### ANNOTATOR ###
-
-about_page <- glue::glue("
-
-## FORRT Replication Database Annotator {packageVersion('FReD')}
-
-**Last Code Update:** {get_last_modified('fred_annotator')}
-
-**Data citation:** {create_citation()}
-
-**Data and Materials:** https://osf.io/9r62x/
-
-**Contribute:** Please send an e-mail to lukas.roeseler(at)uni-muenster.de
-
-**License:** _Data:_ CC-By Attribution 4.0 International, _Code:_ MIT License
-
-**Acknowledgements:** We thank all researchers who have invested resources in conducting replication research, researchers who have submitted their replication studies, and researchers who used the Replication Recipe Post-Completion template to register their results. FORRT Replication Database is supported through the University of Bamberg's Interne Forschungsförderung, by the University of Münster, by the Nederlandse Organisatie voor Wetenschappelijk's (NWO) Open Science Fund, and by the Leuphana University Lüneburg.
-
-**Important note:** This is work in progress. Please beware that there might be bugs or errors in the dataset. If you spot any, please let us know by email, or on GitHub.
-
-_Thanks to our funders:_
-
-
-")
-
-
-welcome_title <- HTML("Welcome to the FORRT Replication Database!")
-welcome_text <- HTML("
The FReD is a collection of crowdsourced findings from replication studies (i.e., studies that investigated one or more previously tested hypotheses using new data). The aims of this project are: -(1) to document replication attempts across many areas of science and make replications findable and -(2) to provide meta-scientists with a database for research on replicability. -This is the FReD Annotator. The annotator reads references (DOIs) from a list and returns replication studies for these references based on the FReD entries. You can go to the online version of the FReD Explorer here. -We advise researchers to carefully investigate replication findings before making judgments about the robustness of research findings. Note that many entries are not yet validated - if you care more about accuracy than coverage, please select the option to only use validated entries. - You can find more information on the FORRT Replication Hub. If you would like to contribute replication findings, please check out our call for results or send us an e-mail.