Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import metadata w/ tests #289

Merged
merged 26 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
85a2644
Write data dictionary.
Nov 27, 2019
7eccdb2
Merge pull request #284 from felixetorres/master
wibeasley Jan 19, 2020
514838b
try to make code exclusions better behaved. Didn't work
wibeasley Jan 19, 2020
c5fefa2
avoid nocov start/stop
wibeasley Jan 19, 2020
e7bb71f
fix codecov stop/end
wibeasley Jan 19, 2020
dcb6db9
retrieve tokens from file
wibeasley Jan 19, 2020
2cf1846
prepare for metadata write
wibeasley Jan 20, 2020
1cd15d9
make @felixetorres's `redcap_metadata_write()` function public
wibeasley Jan 20, 2020
beb7786
tests for `redcap_metadata_write()`
wibeasley Jan 20, 2020
1354579
enclose email in quotes
wibeasley Jan 20, 2020
3f41744
remove unnecessary parameters
wibeasley Jan 20, 2020
5b255d4
Modify doc for `redcap_metadata_write()`
wibeasley Jan 20, 2020
efbac23
include codecov & goodpractice
wibeasley Jan 20, 2020
f9a0f51
tidy
wibeasley Jan 20, 2020
26b2940
test files are faster and more independent
wibeasley Jan 20, 2020
234b838
remove `rev()` on eav column names
wibeasley Jan 20, 2020
e273f3a
bump version and remove 'stats' dependency
wibeasley Jan 20, 2020
dcf638d
test for bad tokens conditions
wibeasley Jan 20, 2020
bd642d3
remove unnecessary dataset reads (not file downloads)
wibeasley Jan 20, 2020
f48ebeb
est for when a file is downloaded twice
wibeasley Jan 20, 2020
9d4119d
test for when a instrument is downloaded twice
wibeasley Jan 20, 2020
b72eada
tidy
wibeasley Jan 20, 2020
e12e511
test bad tokens
wibeasley Jan 20, 2020
8131e86
warn if field names look wrong
wibeasley Jan 21, 2020
de5b0b6
nocov sections
wibeasley Jan 21, 2020
fc99005
update website
wibeasley Jan 21, 2020
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
8 changes: 4 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Description: Encapsulates functions to streamline calls from R to the REDCap
University. The Application Programming Interface (API) offers an avenue
to access and modify data programmatically, improving the capacity for
literate and reproducible programming.
Version: 0.10.2.9004
Version: 0.10.2.9005
Authors@R: c(person("Will", "Beasley", role = c("aut", "cre"), email =
"[email protected]", comment = c(ORCID = "0000-0002-5613-5006")),
person("David", "Bard", role = "ctb"),
Expand All @@ -17,13 +17,13 @@ Authors@R: c(person("Will", "Beasley", role = c("aut", "cre"), email =
role = "ctb"), person("Andrew", "Peters",
role = "ctb"), person("Hao", "Zhu",
role = "ctb",
comment = c(ORCID = '0000-0002-3386-6076')))
comment = c(ORCID = '0000-0002-3386-6076')), person("Felix", "Torres",
role = "ctb", email = "[email protected]"))
URL: https://github.com/OuhscBbmc/REDCapR, http://ouhsc.edu/bbmc/,
http://project-redcap.org
BugReports: https://github.com/OuhscBbmc/REDCapR/issues
Depends:
R(>= 3.0.0),
stats
R(>= 3.0.0)
Imports:
checkmate (>= 1.8.4),
dplyr (>= 0.5.0),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export(redcap_column_sanitize)
export(redcap_download_file_oneshot)
export(redcap_download_instrument)
export(redcap_metadata_read)
export(redcap_metadata_write)
export(redcap_next_free_record_name)
export(redcap_project)
export(redcap_read)
Expand Down
9 changes: 7 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ Upcoming Versions
==========================================================

In the future:
* `redcap_read()` and `redcap_read_oneshot()` allows caller to specify data types for columns.

...

Version 0.11 (Released ?)
==========================================================
Expand All @@ -20,12 +19,18 @@ Version 0.11 (Released ?)

* [`reader::type_convert()`](https://readr.tidyverse.org/reference/type_convert.html) is used *after* all the batches are stacked on top of each other. This way, batches cannot have incompatible data types as they're combined. (#257; thanks @isaactpetersen #245) Consequently, the `guess_max` parameter in `redcap_read()` no longer serves a purpose, and has been soft-deprecated. (#267)

* [`redcap_metadata_write()`](https://readr.tidyverse.org/reference/redcap_metadata_write.html) writes to the project's metadata. (#274, @felixetorres)

### Stability Features

* `httr::content()` (which is inside `kernel_api()`) now processes the returned value as "text/csv", by default. This should prevent strange characters from tricking the process as the internal variable `raw_text` is being formed. See the [httr::content()`](https://httr.r-lib.org/reference/content.html) documentation for a list of possible values for the `content_type` parameter. (Thanks to great debugging by @vortexing, #269)

* Similarly, `kernel_api()` now has an `encoding` parameter, which defaults to "UTF-8". (#270)

### Minor Enhancements

* check for bad field names passed to the read records functions (#288)

### Corrections

* 'checkmate' package is now imported, not suggested (Thanks @dtenenba, #255)
Expand Down
17 changes: 9 additions & 8 deletions R/project-simple.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ populate_project_simple <- function(batch = FALSE) {
"The function REDCapR:::populate_project_simple() cannot run if the ",
"`testthat` package is not installed. Please install it and try again."
)
# nocov stop
# nocov end
}

# Declare the server & user information
uri <- "https://bbmc.ouhsc.edu/redcap/api/"

# token <- "D96029BFCE8FFE76737BFC33C2BCC72E" #For `UnitTestPhiFree` account and the simple project (pid 27) on Vandy's test server.
# token <- "9A81268476645C4E5F03428B8AC3AA7B" #For `UnitTestPhiFree` account and the simple project (pid 153)
token <- "D70F9ACD1EDD6F151C6EA78683944E98" #For `UnitTestPhiFree` account and the simple project (pid 213)
credential <- REDCapR::retrieve_credential_local(
path_credential = system.file("misc/example.credentials", package="REDCapR"),
project_id = 213L
)
uri <- credential$redcap_uri
token <- credential$token

project <- REDCapR::redcap_project$new(redcap_uri = uri, token = token)
path_in_simple <- system.file(
Expand Down Expand Up @@ -77,7 +78,7 @@ clear_project_simple <- function(verbose = TRUE) {
"The function REDCapR:::populate_project_simple() cannot run if the ",
"`testthat` package is not installed. Please install it and try again."
)
# nocov stop
# nocov end
}
path_delete_test_record <-
"https://bbmc.ouhsc.edu/redcap/plugins/redcapr/delete_redcapr_simple.php"
Expand Down Expand Up @@ -106,7 +107,7 @@ clean_start_simple <- function(batch = FALSE, delay_in_seconds = 1) {
"The function REDCapR:::populate_project_simple() cannot run if the ",
"`testthat` package is not installed. Please install it and try again."
)
# nocov stop
# nocov end
}
testthat::expect_message(
clear_result <- clear_project_simple(),
Expand Down
2 changes: 1 addition & 1 deletion R/redcap-metadata-read.R
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ redcap_metadata_read <- function(
kernel$status_code,
kernel$raw_text
)
} # nocov stop
} # nocov end
} else {
ds <- data.frame() #Return an empty data.frame
outcome_message <- sprintf(
Expand Down
136 changes: 136 additions & 0 deletions R/redcap-metadata-write.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#' @title Import metadata
#'
#' @description
#' Import metadata (*i.e.*, data dictionary)
#' into a project. Because of this method's destructive nature,
#' it works for only projects in Development status.
#'
#' @param ds The [base::data.frame()] to be imported into the REDCap project.
#' Required.
#' @param redcap_uri The URI (uniform resource identifier) of the REDCap
#' project. Required.
#' @param token The user-specific string that serves as the password for a
#' project. Required.
#' @param verbose A boolean value indicating if `message`s should be printed
#' to the R console during the operation. The verbose output might contain
#' sensitive information (*e.g.* PHI), so turn this off if the output might
#' be visible somewhere public. Optional.
#' @param config_options A list of options to pass to [httr::POST()] method
#' in the 'httr' package. See the details in [redcap_read_oneshot()] Optional.
#'
#' @return Currently, a list is returned with the following elements:
#' * `success`: A boolean value indicating if the operation was apparently
#' successful.
#' * `status_code`: The
#' [http status code](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
#' of the operation.
#' * `outcome_message`: A human readable string indicating the operation's
#' outcome.
#' * `field_count`: Number of fields imported.
#' * `elapsed_seconds`: The duration of the function.
#' * `raw_text`: If an operation is NOT successful, the text returned by
#' REDCap. If an operation is successful, the `raw_text` is returned as an
#' empty string to save RAM.
#'
#'
#' @author Will Beasley
#'
#' @references The official documentation can be found on the 'API Help Page'
#' and 'API Examples' pages on the REDCap wiki.
#' If you do not have an account for the wiki, please ask your campus REDCap
#' administrator to send you the static material.
#'
#' @examples
#' \dontrun{
#' # Please don't run this example without changing the token to
#' point to your server. It could interfere with our testing suite.
#' uri <- "https://bbmc.ouhsc.edu/redcap/api/"
#' token <- "457C24AB91B7FCF5B1A7DA67E70E24C7"
#'
#' # Read in the dictionary in R's memory from a csv file.
#' ds_to_write <-
#' readr::read_csv(
#' file = system.file(
#' "test-data/project-simple/simple-metadata.csv",
#' package = "REDCapR"
#' ),
#' col_types = readr::cols(.default = readr::col_character())
#' )
#' ds_to_write
#'
#' # Import the dictionary into the REDCap project
#' REDCapR::redcap_metadata_write(
#' ds = ds_to_write,
#' redcap_uri = uri,
#' token = token
#' )
#' }
#'
#' @export
redcap_metadata_write <- function(
ds,
redcap_uri,
token,
verbose = TRUE,
config_options = NULL
) {
csv_elements <- NULL #This prevents the R CHECK NOTE: 'No visible binding for global variable Note in R CMD check'; Also see if( getRversion() >= "2.15.1" ) utils::globalVariables(names=c("csv_elements")) #http://stackoverflow.com/questions/8096313/no-visible-binding-for-global-variable-note-in-r-cmd-check; http://stackoverflow.com/questions/9439256/how-can-i-handle-r-cmd-check-no-visible-binding-for-global-variable-notes-when

checkmate::assert_character(redcap_uri, any.missing = FALSE, len = 1, pattern = "^.{1,}$")
checkmate::assert_character(token , any.missing = FALSE, len = 1, pattern = "^.{1,}$")

token <- sanitize_token(token)
verbose <- verbose_prepare(verbose)

con <- base::textConnection(
object = "csv_elements",
open = "w",
local = TRUE
)
utils::write.csv(ds, con, row.names = FALSE, na = "")
close(con)

csv <- paste(csv_elements, collapse = "\n")
rm(csv_elements, con)

post_body <- list(
token = token,
content = "metadata",
format = "csv",
data = csv,
returnFormat = "csv"
)

# This is the important line that communicates with the REDCap server.
kernel <- kernel_api(redcap_uri, post_body, config_options)

if (kernel$success) {
field_count <- as.integer(kernel$raw_text)
outcome_message <- sprintf(
"%s fields were written to the REDCap dictionary in %0.1f seconds.",
format(field_count, big.mark = ",", scientific = FALSE, trim = TRUE),
kernel$elapsed_seconds
)

# If an operation is successful, the `raw_text` is no longer returned to save RAM. The content is not really necessary with httr's status message exposed.
kernel$raw_text <- ""
} else { # If the returned content wasn't recognized as a valid integer, then
field_count <- 0L
outcome_message <- sprintf(
"The REDCapR write/import metadata operation was not successful. The error message was:\n%s",
kernel$raw_text
)
}

if (verbose)
message(outcome_message)

list(
success = kernel$success,
status_code = kernel$status_code,
outcome_message = outcome_message,
field_count = field_count,
elapsed_seconds = kernel$elapsed_seconds,
raw_text = kernel$raw_text
)
}
6 changes: 4 additions & 2 deletions R/redcap-next-free-record-name.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,23 @@ redcap_next_free_record_name <- function(
value
)

} else { # nocov start
} else {
# nocov start
value <- value_error
outcome_message <- sprintf(
"The REDCap determination of the next free record id failed. The http status code was %i. The 'raw_text' returned was '%s'.",
kernel$status_code,
kernel$raw_text
)
# nocov end
}
} else {
value <- value_error
outcome_message <- sprintf(
"The REDCap determination of the next free record id failed. The error message was:\n%s",
kernel$raw_text
)
} # nocov stop
}

if (verbose)
message(outcome_message)
Expand Down
21 changes: 18 additions & 3 deletions R/redcap-read-oneshot-eav.R
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,18 @@ redcap_read_oneshot_eav <- function(
filter_logic <- filter_logic_prepare(filter_logic)
verbose <- verbose_prepare(verbose)

if (any(grepl("[A-Z]", fields_collapsed)))
if (any(grepl("[A-Z]", fields_collapsed))) {
warning(
"The fields passed to REDCap appear to have at least uppercase letter. ",
"REDCap variable names are snake case."
)
}
if (any(grepl("\\b_", fields_collapsed))) {
warning(
"The fields passed to REDCap appear to start with an underscore, ",
"which is illegal for REDCap."
)
}

post_body <- list(
token = token,
Expand All @@ -207,6 +214,15 @@ redcap_read_oneshot_eav <- function(
# This is the important line that communicates with the REDCap server.
kernel <- kernel_api(redcap_uri, post_body, config_options)

if (!kernel$success) {
error_message <- sprintf(
"The REDCapR record export operation was not successful. The error message was:\n%s",
kernel$raw_text
)
stop(error_message)
}


ds_metadata <-
REDCapR::redcap_metadata_read(
redcap_uri,
Expand Down Expand Up @@ -258,8 +274,7 @@ redcap_read_oneshot_eav <- function(
dplyr::select(field_name = .data$export_field_name) %>%
dplyr::filter(grepl("^\\w+?_complete$", .data$field_name))
) %>%
dplyr::pull(.data$field_name) %>%
rev()
dplyr::pull(.data$field_name) #%>% rev()

ds_eav_2 <-
ds_eav %>%
Expand Down
13 changes: 10 additions & 3 deletions R/redcap-read-oneshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,18 @@ redcap_read_oneshot <- function(
filter_logic <- filter_logic_prepare(filter_logic)
verbose <- verbose_prepare(verbose)

if (any(grepl("[A-Z]", fields_collapsed)))
if (any(grepl("[A-Z]", fields_collapsed))) {
warning(
"The fields passed to REDCap appear to have at least uppercase letter. ",
"REDCap variable names are snake case."
)
}
if (any(grepl("\\b_", fields_collapsed))) {
warning(
"The fields passed to REDCap appear to start with an underscore, ",
"which is illegal for REDCap."
)
}

post_body <- list(
token = token,
Expand Down Expand Up @@ -282,17 +289,17 @@ redcap_read_oneshot <- function(
# message exposed.
kernel$raw_text <- ""
} else { # ds doesn't exist as a data.frame.
# nocov start
# Override the 'success' determination from the http status code.
# and return an empty data.frame.
# nocov start
kernel$success <- FALSE
ds <- data.frame()
outcome_message <- sprintf(
"The REDCap read failed. The http status code was %i. The 'raw_text' returned was '%s'.",
kernel$status_code,
kernel$raw_text
)
# nocov stop
# nocov end
}
} else { # kernel fails
ds <- data.frame() #Return an empty data.frame
Expand Down
11 changes: 10 additions & 1 deletion R/redcap-read.R
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ redcap_read <- function(
config_options = config_options
)

# if (!metadata$success) {
# error_message <- sprintf(
# "The REDCapR record export operation was not successful. The error message was:\n%s",
# metadata$raw_text
# )
# stop(error_message)
# }

initial_call <- REDCapR::redcap_read_oneshot(
redcap_uri = redcap_uri,
token = token,
Expand Down Expand Up @@ -312,7 +320,8 @@ redcap_read <- function(
)

if (continue_on_error) warning(error_message)
else stop(error_message) # nocov stop
else stop(error_message)
# nocov end
}

lst_batch[[i]] <- read_result$data
Expand Down
Loading