Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: REDCapExporter
Title: Automated Construction of R Data Packages from REDCap Projects
Version: 0.3.3
Version: 0.3.4
Authors@R: c(
person(given = "Peter", family = "DeWitt", email = "peter.dewitt@cuanschutz.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-6391-0795"))
)
Expand All @@ -12,8 +12,8 @@ Description: Export all data, including metadata, from a REDCap (Research
default reports are generated as vignettes in the resulting package.
License: GPL-2
Encoding: UTF-8
URL: https://github.com/dewittpe/REDCapExporter, http://www.peteredewitt.com/REDCapExporter/
Language: en-us
URL: https://github.com/dewittpe/REDCapExporter, https://www.peteredewitt.com/REDCapExporter/
Language: en-US
LazyData: true
Depends:
R (>= 3.5.0)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ CRAN = "https://cran.rstudio.com"
SRC = $(wildcard $(PKG_ROOT)/src/*.cpp)
RFILES = $(wildcard $(PKG_ROOT)/R/*.R)
EXAMPLES = $(wildcard $(PKG_ROOT)/examples/*.R)
TESTS = $(wildcard $(PKG_ROOT)/tests/testthat/*.R)
TESTS = $(wildcard $(PKG_ROOT)/tests/test-*.R)

# Targets
VIGNETTES = $(PKG_ROOT)/vignettes/redcap2package.Rmd\
Expand Down
15 changes: 15 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# Version 0.3.4

## Improvements

* Gate `devtools::document()` in `build_r_data_package()` when devtools is not available.

## Testing Change

* Update test-build_r_pkg.R to be robust to platform.
* Update `test-build_r_pkg.R` expectations based on devtools availability.
* Constrain keyring tests to a temporary keyring directory.
* Avoid building/installing a generated package inside the vignette during checks.
* Update test-export to use the REDCapR public dev-2 test endpoint/token and
skip on curl-related failures.

# Version 0.3.3

## Testing Change
Expand Down
6 changes: 5 additions & 1 deletion R/build_r_data_package.R
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,11 @@ build_r_data_package.rcer_rccore <- function(x, path = NULL, author_roles = NULL
file = paste(path, "R/datasets.R", sep = "/")
)

devtools::document(path)
if (requireNamespace("devtools", quietly = TRUE)) {
devtools::document(path)
} else {
message("Skipping devtools::document(): 'devtools' is not available.")
}

invisible(TRUE)
}
Expand Down
28 changes: 26 additions & 2 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
# Version 0.3.4

This submission to CRAN is coming shortly after the prior submission because of
new CRAN check failures.

- Prevent vignette build/install of the generated package (devtools/processx)
during checks.
- Constrain keyring tests to a temporary keyring directory.
- Gate `devtools::document()` inside `build_r_data_package()` when devtools is
not available, and update tests accordingly.
- Update test-export to use the REDCapR public dev-2 test endpoint/token and
skip on curl-related failures.
- Update the personal URL to HTTPS.

## Testing

- Local macOS, R 4.5.2
- R CMD check --as-cran REDCapExporter_0.3.4.tar.gz
- Status: OK (2 NOTEs: no Internet for URL checks, time-stamp check)
- GitHub Actions
- Status: OK
- Windows Builder
- Status: OK
- Rhub
- Status: OK

# Version 0.3.3

- Improve testing suite to account for a resource going offline
Expand Down Expand Up @@ -247,5 +273,3 @@ be used in the examples and vignettes.

> Please fix and resubmit, and document what was changed in the submission
> comments.


2 changes: 1 addition & 1 deletion man/REDCapExporter-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 33 additions & 34 deletions tests/test-as.data.frame.R
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
library(REDCapExporter)

# Expect error if the input in not csv or json. the error should come from the
# read_text call, which is not exported.
x <- avs_raw_metadata
attr(x, "Content-Type") <- c("not-csv", "not-json")
x <- tools::assertError(REDCapExporter:::read_text(x))
stopifnot(identical(x[[1]][["message"]], "Content-Type 'not-csv' is not yet supported."))

# Testing coercion of rcer_raw_metadata to data.frame and data.table
DF0 <- as.data.frame(avs_raw_metadata)
DF1 <- as.data.frame(avs_raw_metadata_json)

stopifnot(all.equal(DF0, DF1))

# Testing coercion of rcer_raw_record to data.frame and data.table
rm(list = ls())

DF0 <- as.data.frame(avs_raw_record)
DF1 <- as.data.frame(avs_raw_record_json)

stopifnot(all.equal(DF0, DF1))

# Testing coercion of rcer_raw_project to data.frame and data.table
rm(list = ls())
DF0 <- as.data.frame(avs_raw_project)
DF1 <- as.data.frame(avs_raw_project_json)

stopifnot(all.equal(DF0, DF1))

# Testing coercion of rcer_raw_user to data.frame and data.table
rm(list = ls())
DF0 <- as.data.frame(avs_raw_user)
DF1 <- as.data.frame(avs_raw_user_json)

stopifnot(all.equal(DF0, DF1))
# Unsupported content type should error
x_bad <- avs_raw_metadata
attr(x_bad, "Content-Type") <- c("not-csv", "not-json")

err <- tryCatch(
REDCapExporter:::read_text(x_bad),
error = function(e) e
)

stopifnot(
inherits(err, "error"),
grepl("Content-Type .* not yet supported", err$message)
)

# Metadata coercion
md_csv <- as.data.frame(avs_raw_metadata)
md_json <- as.data.frame(avs_raw_metadata_json)
stopifnot(isTRUE(all.equal(md_csv, md_json)))

# Record coercion
rec_csv <- as.data.frame(avs_raw_record)
rec_json <- as.data.frame(avs_raw_record_json)
stopifnot(isTRUE(all.equal(rec_csv, rec_json)))

# Project coercion
proj_csv <- as.data.frame(avs_raw_project)
proj_json <- as.data.frame(avs_raw_project_json)
stopifnot(isTRUE(all.equal(proj_csv, proj_json)))

# User coercion
usr_csv <- as.data.frame(avs_raw_user)
usr_json <- as.data.frame(avs_raw_user_json)
stopifnot(isTRUE(all.equal(usr_csv, usr_json)))

92 changes: 66 additions & 26 deletions tests/test-build_r_pkg.R
Original file line number Diff line number Diff line change
@@ -1,34 +1,74 @@
library(REDCapExporter)
temppath <- tempdir()
build_r_data_package(
x = avs_raw_core,
path = temppath,
author_roles = list(dewittp = c("cre", "aut")),
)

x <- fs::dir_tree(temppath)
x <- unname(sapply(strsplit(x, "rcd14465"), `[`, 2))
x[is.na(x)] <- ""
x <- sort(paste0("rcd14465", x))
msgs <- character()
withCallingHandlers(
{
build_r_data_package(
x = avs_raw_core,
path = temppath,
author_roles = list(dewittp = c("cre", "aut"))
)
},
message = function(m) {
msgs <<- c(msgs, conditionMessage(m))
invokeRestart("muffleMessage")
}
)

print(x)
pkgdir <- file.path(temppath, "rcd14465")

# check the DESCRIPTION file for the built package
d <- read.dcf(file.path(pkgdir, "DESCRIPTION"))
stopifnot(
identical(
x,
sort(
c("rcd14465", "rcd14465/DESCRIPTION", "rcd14465/LICENSE", "rcd14465/NAMESPACE",
"rcd14465/R", "rcd14465/R/datasets.R", "rcd14465/data", "rcd14465/data/metadata.rda",
"rcd14465/data/project.rda", "rcd14465/data/record.rda", "rcd14465/data/user.rda",
"rcd14465/inst", "rcd14465/inst/raw-data", "rcd14465/inst/raw-data/metadata.rds",
"rcd14465/inst/raw-data/project.rds", "rcd14465/inst/raw-data/record.rds",
"rcd14465/inst/raw-data/user.rds", "rcd14465/man", "rcd14465/man/metadata.Rd",
"rcd14465/man/project.Rd", "rcd14465/man/record.Rd", "rcd14465/man/user.Rd")
)
)
d[1, "Package"] == "rcd14465",
grepl("\\d{4}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}", d[1, "Version"])
)

stopifnot(
packageDescription(pkg = "rcd14465", lib.loc = temppath)$Package == "rcd14465",
grepl("\\d{4}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}", packageDescription(pkg = "rcd14465", lib.loc = temppath)$Version)
)
# check the file structure of the built package
x <- list.files(
path = pkgdir,
all.files = TRUE,
recursive = TRUE,
include.dirs = TRUE,
full.names = FALSE,
no.. = TRUE
)

# switch slashes from windows to *nix
x <- gsub("\\\\", "/", x)

expected <- sort(c(
"DESCRIPTION",
"LICENSE",
"R",
"R/datasets.R",
"data",
"data/metadata.rda",
"data/project.rda",
"data/record.rda",
"data/user.rda",
"inst",
"inst/raw-data",
"inst/raw-data/metadata.rds",
"inst/raw-data/project.rds",
"inst/raw-data/record.rds",
"inst/raw-data/user.rds"
))

if (requireNamespace("devtools", quietly = TRUE)) {
expected <- sort(c(
expected,
"NAMESPACE",
"man",
"man/metadata.Rd",
"man/project.Rd",
"man/record.Rd",
"man/user.Rd"
))
stopifnot(!any(grepl("Skipping devtools::document", msgs)))
} else {
stopifnot(any(grepl("Skipping devtools::document", msgs)))
}

stopifnot(identical(x, expected))
106 changes: 23 additions & 83 deletions tests/test-export.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
library(REDCapExporter)

# Tests rely on publicly available REDCap and tokens published
# https://github.com/redcap-tools/redcap-test-datasets/tree/master
# Tests rely on publicly available REDCap test datasets and tokens.
# The REDCapR project moved its public test credentials from bbmc.ouhsc.edu
# to redcap-dev-2.ouhsc.edu in February 2026 (see REDCapR repo history and
# inst/misc/example.credentials). We follow that public test endpoint here.
# The API tokens below are intended to be public and used for testing.

# project_name token server_url pid server read_only operational notes
# archer 9A81268476645C4E5F03428B8AC3AA7B https://bbmc.ouhsc.edu/redcap/api/ 153 oklahoma-bbmc TRUE TRUE simple structure; read-only
# archer D70F9ACD1EDD6F151C6EA78683944E98 https://bbmc.ouhsc.edu/redcap/api/ 213 oklahoma-bbmc FALSE TRUE simple structure; read & write
# archer 0434F0E9CF53ED0587847AB6E51DE762 https://bbmc.ouhsc.edu/redcap/api/ 212 oklahoma-bbmc TRUE TRUE longitudinal structure; read-only
# archer D72C6485B52FE9F75D27B696977FBA43 https://bbmc.ouhsc.edu/redcap/api/ 268 oklahoma-bbmc TRUE TRUE Russian characters; read-only
# project_name token server_url pid server read_only operational notes
# simple 9A068C425B1341D69E83064A2D273A70 https://redcap-dev-2.ouhsc.edu/redcap/api/ 33 ouhsc-dev-2 TRUE TRUE simple structure; read-only

on_cran <- function() {
# copied from testthat:::on_cran
Expand All @@ -22,84 +22,24 @@ on_cran <- function() {
if (!on_cran()) {
archer01_csv <-
tryCatch(
export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = '9A81268476645C4E5F03428B8AC3AA7B'),
export_core(uri = 'https://redcap-dev-2.ouhsc.edu/redcap/api/', token = '9A068C425B1341D69E83064A2D273A70'),
error = function(e) {e}
)

expected_return <-
c(
project_raw = "ERROR: You do not have permissions to use the API",
metadata_raw = "ERROR: You do not have permissions to use the API",
user_raw = "ERROR: You do not have permissions to use the API",
record_raw = "ERROR: You do not have permissions to use the API"
)

stopifnot(
!inherits(archer01_csv, "error"), # not an error - a retun happened, but it is not a useful return
length(archer01_csv) == 4L,
inherits(archer01_csv[[1]], "rcer_raw_project"),
inherits(archer01_csv[[2]], "rcer_raw_metadata"),
inherits(archer01_csv[[3]], "rcer_raw_user"),
inherits(archer01_csv[[4]], "rcer_raw_record"),
identical(sapply(archer01_csv, getElement, 1), expected_return)
)
if (inherits(archer01_csv, "error")) {
if (inherits(archer01_csv, "curl_error")) {
message(sprintf("Skip test-export.R after curl error: %s", archer01_csv$message))
} else {
stop("in test-export.R a non-curl error occurred when calling export_core")
}
} else {
stopifnot(
length(archer01_csv) == 4L,
inherits(archer01_csv[[1]], "rcer_raw_project"),
inherits(archer01_csv[[2]], "rcer_raw_metadata"),
inherits(archer01_csv[[3]], "rcer_raw_user"),
inherits(archer01_csv[[4]], "rcer_raw_record")
)
}
}

################################################################################
# OLD TESTS, for version < 0.3.3 these tests were used, but in Feb 2026 the API
# failed and so do the tests. Not a REDCapExporter issue, an RedCap API issue.
#
# Commented out for now until a fix can be made.
#
###
###
### Sys.setenv("REDCap_API_URI" = 'https://bbmc.ouhsc.edu/redcap/api/')
### Sys.setenv("REDCap_API_TOKEN" = '9A81268476645C4E5F03428B8AC3AA7B')
### archer01_json <- tryCatch(export_core(format = "json"), error = function(e) {e})
###
### if (inherits(archer01_csv, "error") | inherits(archer01_json, "error")) {
### if (inherits(archer01_csv, "curl_error") | inherits(archer01_json, "curl_error")) {
### # skip the rest of the testing
### print(sprintf("Skip test-export.R after curl error: %s", archer01_csv$message))
### } else {
### stop("in test-export.R an non curl related error occured when initially calling export_core")
### }
### } else {
###
### stopifnot(
### inherits(archer01_csv, "rcer_rccore"),
### inherits(archer01_json, "rcer_rccore"),
### inherits(archer01_csv$project_raw, "rcer_raw_project"),
### inherits(archer01_json$project_raw, "rcer_raw_project"),
### inherits(archer01_csv$metadata_raw, "rcer_raw_metadata"),
### inherits(archer01_json$metadata_raw, "rcer_raw_metadata"),
### inherits(archer01_csv$project_raw, "rcer_raw_project"),
### inherits(archer01_json$project_raw, "rcer_raw_project"),
### inherits(archer01_csv$record_raw, "rcer_raw_record"),
### inherits(archer01_json$record_raw, "rcer_raw_record"),
### grepl("text/csv", attr(archer01_csv$record_raw, "Content-Type")),
### grepl("application/json", attr(archer01_json$record_raw, "Content-Type"))
### )
###
### a1 <- format_record(archer01_csv)
### a2 <- format_record(archer01_json)
###
### # apparently the end of line characters are exported differently
### a1$address <- gsub('\n', ' ', gsub('\r', '', a1$address))
### a2$address <- gsub('\n', ' ', gsub('\r', '', a2$address))
### a1$comments <- gsub('\n', ' ', gsub('\r', '', a1$comments))
### a2$comments <- gsub('\n', ' ', gsub('\r', '', a2$comments))
###
### stopifnot(isTRUE(all.equal(a1, a2)))
###
### }
###
### #
### #archer02 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = 'D70F9ACD1EDD6F151C6EA78683944E98')
### #archer02 <- format_record(archer02)
### #
### #archer03 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = '0434F0E9CF53ED0587847AB6E51DE762')
### #archer03 <- format_record(archer03)
### #
### #archer04 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = 'D72C6485B52FE9F75D27B696977FBA43')
### #archer04 <- format_record(archer04)
Loading