Skip to content

Commit e612caf

Browse files
authored
Merge pull request #382 from joundso/read-data-access-groups
Adds function to export available data access groups
2 parents 9134a76 + 2695739 commit e612caf

File tree

5 files changed

+305
-2
lines changed

5 files changed

+305
-2
lines changed

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export(create_batch_glossary)
1010
export(create_credential_local)
1111
export(redcap_arm_export)
1212
export(redcap_column_sanitize)
13+
export(redcap_dag_read)
1314
export(redcap_delete)
1415
export(redcap_download_file_oneshot)
1516
export(redcap_download_instrument)

NEWS.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Upcoming Versions
88
* `redcap_read()` and `redcap_read_oneshot()` accept a new `locale` parameter that specifies date, time, and number formats, like using a comma as the decimal separator. It is a [`readr::locale`](https://readr.tidyverse.org/reference/locale.html) object. (#377, suggested by @joundso)
99
* New `redcap_instruments()` function exports a list of the data collection instruments for a project. (#381, @vcastro)
1010
* New `redcap_event_instruments()` function exports the instrument-event mappings for a project (i.e., how the data collection instruments are designated for certain events in a longitudinal project).. (#381, @vcastro)
11+
* New `redcap_dag_read()` function returns the Data Access Groups for a project (#382, @joundso)
1112
* New detection when REDCap has trouble with a large request and drops records. (#400 w/ @TimMonahan)
1213

1314
### Minor Enhancements
@@ -23,7 +24,7 @@ Upcoming Versions
2324
* For the testing server & projects, the http errors are a little different, so the testing code was adjusted (#396)
2425
* Set `httr::user_agent`, following the advice of httr's vignette (#397)
2526

26-
### Test Suite
27+
### Test Suite
2728

2829
* Added two more dictionaries that are super wide -5k & 35k variables (#335 & #360, @januz & @datalorax)
2930
* Read, modify, & read projects with DAGs (#353, daniela.wolkersdorfer, #353)
@@ -71,7 +72,7 @@ Version 0.11.0 (Released 2020-04-20)
7172

7273
* [`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)
7374

74-
* [`redcap_metadata_write()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_metadata_write.html) writes to the project's metadata. (#274, @felixetorres)
75+
* [`redcap_metadata_write()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_metadata_write.html) writes to the project's metadata. (#274, @felixetorres)
7576

7677
* [`redcap_survey_link_export_oneshot()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_survey_link_export_oneshot.html) retrieves the URL to a specific record's survey (*e.g.*, "https://bbmc.ouhsc.edu/redcap/surveys/?s=8KuzSLMHf6") (#293)
7778

R/redcap-read-dag.R

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#' @title Read data access groups from a REDCap project
2+
#'
3+
#' @description This function reads all available data access groups from
4+
#' REDCap an returns them as a [base::data.frame()].
5+
#'
6+
#' @param redcap_uri The URI (uniform resource identifier) of the REDCap
7+
#' project. Required.
8+
#' @param token The user-specific string that serves as the password for a
9+
#' project. Required.
10+
#' @param http_response_encoding The encoding value passed to
11+
#' [httr::content()]. Defaults to 'UTF-8'.
12+
#' @param locale a [readr::locale()] object to specify preferences like
13+
#' number, date, and time formats. This object is passed to
14+
#' [`readr::read_csv()`]. Defaults to [readr::default_locale()].
15+
#' @param verbose A boolean value indicating if `message`s should be printed
16+
#' to the R console during the operation. The verbose output might contain
17+
#' sensitive information (*e.g.* PHI), so turn this off if the output might
18+
#' be visible somewhere public. Optional.
19+
#' @param config_options A list of options to pass to `POST` method in the
20+
#' `httr` package.
21+
#'
22+
#' @return Currently, a list is returned with the following elements:
23+
#' * `data`: An R [base::data.frame()] of all data access groups of the project.
24+
#' * `success`: A boolean value indicating if the operation was apparently
25+
#' successful.
26+
#' * `status_codes`: A collection of
27+
#' [http status codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes),
28+
#' separated by semicolons. There is one code for each batch attempted.
29+
#' * `outcome_messages`: A collection of human readable strings indicating the
30+
#' operations' semicolons. There is one code for each batch attempted. In an
31+
#' unsuccessful operation, it should contain diagnostic information.
32+
#' * `elapsed_seconds`: The duration of the function.
33+
#'
34+
#'
35+
#' @author Jonathan M. Mang
36+
#' @references The official documentation can be found on the 'API Help Page'
37+
#' and 'API Examples' pages on the REDCap wiki (*i.e.*,
38+
#' https://community.projectredcap.org/articles/456/api-documentation.html
39+
#' and
40+
#' https://community.projectredcap.org/articles/462/api-examples.html).
41+
#' If you do not have an account for the wiki, please ask your campus REDCap
42+
#' administrator to send you the static material.
43+
#'
44+
#' @examples
45+
#' \dontrun{
46+
#' uri <- "https://bbmc.ouhsc.edu/redcap/api/"
47+
#' token <- "9A81268476645C4E5F03428B8AC3AA7B"
48+
#' REDCapR::redcap_dag_read(redcap_uri=uri, token=token)$data
49+
#' }
50+
51+
#' @export
52+
redcap_dag_read <- function(
53+
redcap_uri,
54+
token,
55+
http_response_encoding = "UTF-8",
56+
locale = readr::default_locale(),
57+
verbose = TRUE,
58+
config_options = NULL
59+
) {
60+
checkmate::assert_character(redcap_uri , any.missing=FALSE, len=1, pattern="^.{1,}$")
61+
checkmate::assert_character(token , any.missing=FALSE, len=1, pattern="^.{1,}$")
62+
checkmate::assert_character(http_response_encoding , any.missing=FALSE, len=1)
63+
checkmate::assert_class( locale, "locale" , null.ok = FALSE)
64+
65+
checkmate::assert_logical( verbose , any.missing=FALSE, len=1, null.ok=TRUE)
66+
checkmate::assert_list( config_options , any.missing=TRUE , null.ok=TRUE)
67+
68+
69+
token <- sanitize_token(token)
70+
verbose <- verbose_prepare(verbose)
71+
72+
post_body <- list(
73+
token = token,
74+
content = "dag",
75+
format = "csv"
76+
)
77+
78+
# This is the important line that communicates with the REDCap server.
79+
kernel <- kernel_api(
80+
redcap_uri = redcap_uri,
81+
post_body = post_body,
82+
config_options = config_options,
83+
encoding = http_response_encoding
84+
)
85+
86+
if (kernel$success) {
87+
try(
88+
# Convert the raw text to a dataset.
89+
ds <-
90+
readr::read_csv(
91+
file = I(kernel$raw_text),
92+
locale = locale,
93+
show_col_types = FALSE
94+
) %>%
95+
as.data.frame(),
96+
97+
# Don't print the warning in the try block. Print it below,
98+
# where it's under the control of the caller.
99+
silent = TRUE
100+
)
101+
102+
if (exists("ds") & inherits(ds, "data.frame")) {
103+
outcome_message <- sprintf(
104+
"%s data access groups were read from REDCap in %0.1f seconds. The http status code was %i.",
105+
format( nrow(ds), big.mark = ",", scientific = FALSE, trim = TRUE),
106+
kernel$elapsed_seconds,
107+
kernel$status_code
108+
)
109+
110+
# ds <- dplyr::mutate_if(
111+
# ds,
112+
# is.character,
113+
# function(x) dplyr::coalesce(x, "") #Replace NAs with blanks
114+
# )
115+
#
116+
# ds <- dplyr::mutate_if(
117+
# ds,
118+
# is.character,
119+
# function( x ) gsub("\r\n", "\n", x, perl=TRUE)
120+
# )
121+
# ds <- dplyr::mutate_if(
122+
# ds,
123+
# function( x) inherits(x, "Date"),
124+
# as.character
125+
# )
126+
#
127+
# ds <- base::as.data.frame(ds)
128+
129+
# If an operation is successful, the `raw_text` is no longer returned to
130+
# save RAM. The content is not really necessary with httr's status
131+
# message exposed.
132+
kernel$raw_text <- ""
133+
} else { # ds doesn't exist as a data.frame.
134+
# nocov start
135+
# Override the 'success' determination from the http status code.
136+
# and return an empty data.frame.
137+
kernel$success <- FALSE
138+
ds <- data.frame()
139+
outcome_message <- sprintf(
140+
"The REDCap read failed. The http status code was %i. The 'raw_text' returned was '%s'.",
141+
kernel$status_code,
142+
kernel$raw_text
143+
)
144+
# nocov end
145+
}
146+
} else { # kernel fails
147+
ds <- data.frame() #Return an empty data.frame
148+
outcome_message <- if (any(grepl(kernel$regex_empty, kernel$raw_text))) {
149+
"The REDCapR read/export operation was not successful. The returned dataset was empty." # nocov
150+
} else {
151+
sprintf(
152+
"The REDCapR read/export operation was not successful. The error message was:\n%s",
153+
kernel$raw_text
154+
)
155+
}
156+
}
157+
158+
if (verbose)
159+
message(outcome_message)
160+
161+
list(
162+
data = ds,
163+
success = kernel$success,
164+
status_code = kernel$status_code,
165+
outcome_message = outcome_message,
166+
elapsed_seconds = kernel$elapsed_seconds,
167+
raw_text = kernel$raw_text
168+
)
169+
}

man/redcap_dag_read.Rd

+75
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-dag-read.R

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
library(testthat)
2+
3+
credential_1 <- retrieve_credential_testing()
4+
credential_no_dag <- retrieve_credential_testing(2597L)
5+
6+
7+
test_that("smoke", {
8+
testthat::skip_on_cran()
9+
expect_message(
10+
returned <- redcap_dag_read(
11+
redcap_uri = credential_1$redcap_uri,
12+
token = credential_1$token
13+
)
14+
)
15+
})
16+
17+
test_that("dag-default", {
18+
testthat::skip_on_cran()
19+
expected_data <-
20+
structure(list(data_access_group_name = c("dag_1", "dag_2"),
21+
unique_group_name = c("dag_1", "dag_2")), row.names = c(NA,
22+
-2L), class = "data.frame"
23+
)
24+
expect_message(
25+
actual <- redcap_dag_read(
26+
redcap_uri = credential_1$redcap_uri,
27+
token = credential_1$token
28+
)
29+
)
30+
31+
expect_true( actual$success)
32+
expect_equal(actual$status_code, 200L)
33+
expect_equal(actual$data, expected_data)
34+
})
35+
36+
test_that("dag-default", {
37+
testthat::skip_on_cran()
38+
expected_data <-
39+
structure(list(data_access_group_name = character(0),
40+
unique_group_name = character(0)), row.names = integer(0),
41+
class = "data.frame"
42+
)
43+
44+
expect_message(
45+
actual <- redcap_dag_read(
46+
redcap_uri = credential_no_dag$redcap_uri,
47+
token = credential_no_dag$token
48+
)
49+
)
50+
51+
expect_true( actual$success)
52+
expect_equal(actual$status_code, 200L)
53+
expect_equal(actual$data, expected_data)
54+
})
55+
56+
rm(credential_1)
57+
rm(credential_no_dag)

0 commit comments

Comments
 (0)