diff --git a/DESCRIPTION b/DESCRIPTION
index 4f3e35a4..7f1986cc 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -65,7 +65,7 @@ Suggests:
License: MIT + file LICENSE
VignetteBuilder: knitr
Encoding: UTF-8
-RoxygenNote: 7.3.1
+RoxygenNote: 7.3.2
Roxygen: list(markdown = TRUE)
Config/testthat/edition: 3
Language: en-US
diff --git a/NEWS.md b/NEWS.md
index dd43ec7e..cf3b6bc6 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -152,6 +152,7 @@ The package has been stable for years and should be reflected in the major versi
* Accepts more than one `config_option` element. (Proposed by @BastienRance, #307)
* Fixed calculation of `success` value returned by `redcap_read()` and `redcap_write()` when the parameter `continue_on_error` is true. (Bug found by @llrs, #317)
+* [`redcap_survey_link_export_oneshot()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_survey_link_export_oneshot.html) documentation corrected & improved (@jrob95, #526)
Version 0.11.0 (Released 2020-04-20)
==========================================================
diff --git a/R/REDCapR-package.R b/R/REDCapR-package.R
index b196d290..a03dc91b 100644
--- a/R/REDCapR-package.R
+++ b/R/REDCapR-package.R
@@ -14,7 +14,7 @@
#' following projects. We appreciate the support.
#' * *OUHSC CCAN Independent Evaluation of the State of Oklahoma Competitive
#' Maternal, Infant, and Early Childhood Home Visiting
-#' ([MIECHV](https://mchb.hrsa.gov/maternal-child-health-initiatives/home-visiting-overview)) Project*.
+#' ([MIECHV](https://mchb.hrsa.gov/programs-impact/programs/home-visiting/maternal-infant-early-childhood-home-visiting-miechv-program)) Project*.
#' HRSA/ACF D89MC23154.
#' David Bard, PI, OUHSC; 2011-2015.
#' * *Independent Evaluation of the State of OK MIECHV Evidence Based Home
diff --git a/R/helpers-testing.R b/R/helpers-testing.R
index 24f1b591..6229db56 100644
--- a/R/helpers-testing.R
+++ b/R/helpers-testing.R
@@ -1,6 +1,6 @@
retrieve_credential_testing <- function(project_id = 153L, username = NA_character_) {
checkmate::assert_integer(project_id, lower = 1, len = 1, any.missing = FALSE)
- REDCapR::retrieve_credential_local(
+ REDCapR::retrieve_credential_local(
path_credential = system.file("misc/example.credentials", package = "REDCapR"),
project_id = project_id,
username = username
diff --git a/R/project-dag-write.R b/R/project-dag-write.R
index bb125516..207b9343 100644
--- a/R/project-dag-write.R
+++ b/R/project-dag-write.R
@@ -46,23 +46,23 @@ populate_project_dag_write <- function(batch = FALSE, verbose = FALSE) {
# Import the data into the REDCap project
# testthat::expect_message(
- returned_object <- if (batch) {
- REDCapR::redcap_write(
- ds_to_write = ds_to_write,
- redcap_uri = project$redcap_uri,
- token = project$token,
- verbose = verbose,
- convert_logical_to_integer = TRUE
- )
- } else {
- REDCapR::redcap_write_oneshot(
- ds = ds_to_write,
- redcap_uri = project$redcap_uri,
- token = project$token,
- verbose = verbose,
- convert_logical_to_integer = TRUE
- )
- }
+ returned_object <- if (batch) {
+ REDCapR::redcap_write(
+ ds_to_write = ds_to_write,
+ redcap_uri = project$redcap_uri,
+ token = project$token,
+ verbose = verbose,
+ convert_logical_to_integer = TRUE
+ )
+ } else {
+ REDCapR::redcap_write_oneshot(
+ ds = ds_to_write,
+ redcap_uri = project$redcap_uri,
+ token = project$token,
+ verbose = verbose,
+ convert_logical_to_integer = TRUE
+ )
+ }
# )
# # If uploading the data was successful, then upload the image files.
diff --git a/R/redcap-delete.R b/R/redcap-delete.R
index a222f60e..c1e1d4eb 100644
--- a/R/redcap-delete.R
+++ b/R/redcap-delete.R
@@ -178,15 +178,15 @@ redcap_delete <- function(
kernel$raw_text <- ""
} else {
records_affected_count <- 0
- error_message <- sprintf(
- paste(
- "The REDCapR record deletion failed.",
- "The http status code was %i.",
- "The error message was: '%s'."
- ),
- kernel$status_code,
- kernel$raw_text
- )
+ error_message <- sprintf(
+ paste(
+ "The REDCapR record deletion failed.",
+ "The http status code was %i.",
+ "The error message was: '%s'."
+ ),
+ kernel$status_code,
+ kernel$raw_text
+ )
stop(error_message)
}
} else {
diff --git a/R/redcap-event-read.R b/R/redcap-event-read.R
index e8099d56..fffbba89 100644
--- a/R/redcap-event-read.R
+++ b/R/redcap-event-read.R
@@ -72,11 +72,11 @@
#' }
#' @export
redcap_event_read <- function(
- redcap_uri,
- token,
- verbose = TRUE,
- config_options = NULL,
- handle_httr = NULL
+ redcap_uri,
+ token,
+ verbose = TRUE,
+ config_options = NULL,
+ handle_httr = NULL
) {
checkmate::assert_character(redcap_uri, any.missing=FALSE, len=1, pattern="^.{1,}$")
diff --git a/R/redcap-log-read.R b/R/redcap-log-read.R
index ba36dea4..012ec5b9 100644
--- a/R/redcap-log-read.R
+++ b/R/redcap-log-read.R
@@ -168,7 +168,6 @@ redcap_log_read <- function(
# where it's under the control of the caller.
silent = TRUE
)
- # browser()
if (exists("ds") && inherits(ds, "data.frame")) {
outcome_message <- sprintf(
diff --git a/R/redcap-metadata-coltypes.R b/R/redcap-metadata-coltypes.R
index 3f6eaf7f..7c006b77 100644
--- a/R/redcap-metadata-coltypes.R
+++ b/R/redcap-metadata-coltypes.R
@@ -209,14 +209,14 @@ redcap_metadata_coltypes <- function(
#' @importFrom magrittr %>%
redcap_metadata_internal <- function(
- redcap_uri,
- token,
-
- http_response_encoding = "UTF-8",
- locale = readr::default_locale(),
- verbose = FALSE,
- config_options = NULL,
- handle_httr = NULL
+ redcap_uri,
+ token,
+
+ http_response_encoding = "UTF-8",
+ locale = readr::default_locale(),
+ verbose = FALSE,
+ config_options = NULL,
+ handle_httr = NULL
) {
checkmate::assert_character(redcap_uri , any.missing=FALSE, len=1, pattern="^.{1,}$")
diff --git a/R/redcap-project-info-read.R b/R/redcap-project-info-read.R
index edcdfed6..3cc78af4 100644
--- a/R/redcap-project-info-read.R
+++ b/R/redcap-project-info-read.R
@@ -218,8 +218,9 @@ redcap_project_info_read <- function(
# Build a column specification that matches the API response.
col_types <- readr::cols()
- for (present_name in present_names)
+ for (present_name in present_names) {
col_types$cols <- c(col_types$cols, all_col_types$cols[present_name])
+ }
# Convert the raw text to a dataset.
ds <-
diff --git a/R/redcap-read-eav-oneshot.R b/R/redcap-read-eav-oneshot.R
index 478d6f33..88b8ec4b 100644
--- a/R/redcap-read-eav-oneshot.R
+++ b/R/redcap-read-eav-oneshot.R
@@ -172,31 +172,31 @@
#' @importFrom magrittr %>%
# Intentionally NOT exporting for now: @export
redcap_read_eav_oneshot <- function(
- redcap_uri,
- token,
- records = NULL,
- fields = NULL,
- forms = NULL,
- events = NULL,
- # raw_or_label = "raw",
- # raw_or_label_headers = "raw",
- # export_checkbox_label = FALSE,
- # placeholder returnFormat
- export_survey_fields = FALSE,
- export_data_access_groups = FALSE,
- filter_logic = "",
- datetime_range_begin = as.POSIXct(NA),
- datetime_range_end = as.POSIXct(NA),
- blank_for_gray_form_status = FALSE,
- # col_types = NULL,
- # guess_type = TRUE,
- # guess_max = 1000,
- http_response_encoding = "UTF-8",
- locale = readr::default_locale(),
- verbose = TRUE,
- config_options = NULL,
- handle_httr = NULL
- # encode_httr = "multipart"
+ redcap_uri,
+ token,
+ records = NULL,
+ fields = NULL,
+ forms = NULL,
+ events = NULL,
+ # raw_or_label = "raw",
+ # raw_or_label_headers = "raw",
+ # export_checkbox_label = FALSE,
+ # placeholder returnFormat
+ export_survey_fields = FALSE,
+ export_data_access_groups = FALSE,
+ filter_logic = "",
+ datetime_range_begin = as.POSIXct(NA),
+ datetime_range_end = as.POSIXct(NA),
+ blank_for_gray_form_status = FALSE,
+ # col_types = NULL,
+ # guess_type = TRUE,
+ # guess_max = 1000,
+ http_response_encoding = "UTF-8",
+ locale = readr::default_locale(),
+ verbose = TRUE,
+ config_options = NULL,
+ handle_httr = NULL
+ # encode_httr = "multipart"
) {
checkmate::assert_character(redcap_uri , any.missing=FALSE, len=1, pattern="^.{1,}$")
@@ -205,11 +205,11 @@ redcap_read_eav_oneshot <- function(
checkmate::assert_character(fields , any.missing=TRUE , min.len=1, pattern="^.{1,}$", null.ok=TRUE)
checkmate::assert_character(forms , any.missing=TRUE , min.len=1, pattern="^.{1,}$", null.ok=TRUE)
checkmate::assert_character(events , any.missing=TRUE , min.len=1, pattern="^.{1,}$", null.ok=TRUE)
-# checkmate::assert_character(raw_or_label , any.missing=FALSE, len=1)
-# checkmate::assert_subset( raw_or_label , c("raw", "label"))
-# checkmate::assert_character(raw_or_label_headers , any.missing=FALSE, len=1)
-# checkmate::assert_subset( raw_or_label_headers , c("raw", "label"))
-# checkmate::assert_logical( export_checkbox_label , any.missing=FALSE, len=1)
+ # checkmate::assert_character(raw_or_label , any.missing=FALSE, len=1)
+ # checkmate::assert_subset( raw_or_label , c("raw", "label"))
+ # checkmate::assert_character(raw_or_label_headers , any.missing=FALSE, len=1)
+ # checkmate::assert_subset( raw_or_label_headers , c("raw", "label"))
+ # checkmate::assert_logical( export_checkbox_label , any.missing=FALSE, len=1)
# placeholder: returnFormat
checkmate::assert_logical( export_survey_fields , any.missing=FALSE, len=1)
checkmate::assert_logical( export_data_access_groups , any.missing=FALSE, len=1)
@@ -218,8 +218,8 @@ redcap_read_eav_oneshot <- function(
checkmate::assert_posixct( datetime_range_end , any.missing=TRUE , len=1, null.ok=TRUE)
checkmate::assert_logical( blank_for_gray_form_status , any.missing=FALSE, len=1)
-# checkmate::assert_logical( guess_type , any.missing=FALSE, len=1)
-# checkmate::assert_numeric( guess_max , any.missing=FALSE, len=1, lower=1)
+ # checkmate::assert_logical( guess_type , any.missing=FALSE, len=1)
+ # checkmate::assert_numeric( guess_max , any.missing=FALSE, len=1, lower=1)
checkmate::assert_character(http_response_encoding , any.missing=FALSE, len=1)
checkmate::assert_class( locale, "locale" , null.ok = FALSE)
@@ -284,7 +284,6 @@ redcap_read_eav_oneshot <- function(
readr::read_csv(
file = I(kernel$raw_text),
col_types = readr::cols(.default = readr::col_character()),
- # guess_max = guess_max,
locale = locale,
show_col_types = FALSE
) %>%
diff --git a/R/redcap-read-oneshot-eav.R b/R/redcap-read-oneshot-eav.R
index 2202f885..428cbee3 100644
--- a/R/redcap-read-oneshot-eav.R
+++ b/R/redcap-read-oneshot-eav.R
@@ -328,7 +328,7 @@ redcap_read_oneshot_eav <- function(
) %>%
dplyr::full_join(ds_possible_checkbox_rows, by=c("record", "field_name", "field_type", "event_id")) %>%
dplyr::mutate(
- value =
+ value =
dplyr::if_else(
!is.na(.data$field_type) & (.data$field_type == "checkbox"),
as.character(!is.na(.data$value)),
diff --git a/R/redcap-read.R b/R/redcap-read.R
index 683b34c2..3f0cef52 100644
--- a/R/redcap-read.R
+++ b/R/redcap-read.R
@@ -324,7 +324,7 @@ redcap_read <- function(
if (!is.null(events)) {
if (!metadata$longitudinal) {
"This project is NOT longitudinal, so do not pass a value to the `event` argument." %>%
- stop(call. = FALSE)
+ stop(call. = FALSE)
} else {
events_in_project <-
redcap_event_read(
diff --git a/R/redcap-survey-link-export-oneshot.R b/R/redcap-survey-link-export-oneshot.R
index 718d8036..e7005bae 100644
--- a/R/redcap-survey-link-export-oneshot.R
+++ b/R/redcap-survey-link-export-oneshot.R
@@ -1,8 +1,8 @@
#' @title
-#' Download a file from a REDCap project record
+#' Get survey link from REDCap
#'
#' @description
-#' This function uses REDCap's API to download a file.
+#' This function uses REDCap's API to get the link for a survey.
#'
#' @param redcap_uri The
#' [uri](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)/url
@@ -11,10 +11,12 @@
#' Required.
#' @param token The user-specific string that serves as the password for a
#' project. Required.
-#' @param record The record ID where the file is to be imported. Required
+#' @param record The record ID associated with the
+#' survey link. Required
#' @param instrument The name of the instrument associated with the
#' survey link. Required
-#' @param event The name of the event where the file is saved in REDCap.
+#' @param event The name of the event associated with the
+#' survey link.
#' Optional
#' @param verbose A boolean value indicating if `message`s should be printed
#' to the R console during the operation. Optional.
@@ -27,6 +29,7 @@
#'
#' @return
#' Currently, a list is returned with the following elements,
+#' * `survey_link`: a character string containing the URL for the survey.
#' * `success`: A boolean value indicating if the operation was apparently
#' successful.
#' * `status_code`: The
@@ -34,14 +37,14 @@
#' of the operation.
#' * `outcome_message`: A human readable string indicating the operation's
#' outcome.
-#' * `records_affected_count`: The number of records inserted or updated.
-#' * `affected_ids`: The subject IDs of the inserted or updated records.
+#' * `instrument`: The instrument associated with the survey link.
+#' * `records_affected_count`: The number of records associated with
+#' the survey link.
+#' * `affected_ids`: The subject IDs associated with the survey link.
#' * `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.
-#' * `file_name`: The name of the file persisted to disk. This is useful if
-#' the name stored in REDCap is used (which is the default).
#'
#' @details
#' Currently, the function doesn't modify any variable types to conform to
diff --git a/R/redcap-users-export.R b/R/redcap-users-export.R
index 923c617c..e97d36fa 100644
--- a/R/redcap-users-export.R
+++ b/R/redcap-users-export.R
@@ -100,11 +100,13 @@ redcap_users_export <- function(
data_import_tool = readr::col_logical(),
data_comparison_tool = readr::col_logical(),
logging = readr::col_logical(),
+ email_logging = readr::col_logical(), # added 14.6.0
file_repository = readr::col_logical(),
data_quality_create = readr::col_logical(),
data_quality_execute = readr::col_logical(),
api_export = readr::col_logical(),
api_import = readr::col_logical(),
+ api_modules = readr::col_logical(), # added 14.6.0 maybe
mobile_app = readr::col_logical(),
mobile_app_download_data = readr::col_logical(),
record_create = readr::col_logical(),
diff --git a/R/validate.R b/R/validate.R
index 5518a600..b6a296b1 100644
--- a/R/validate.R
+++ b/R/validate.R
@@ -247,8 +247,8 @@ assert_field_names <- function(field_names) {
"a letter. The bad names are {%s}.",
collapse = ""
) %>%
- sprintf(length(bad_names), paste(bad_names, collapse = ", ")) %>%
- stop()
+ sprintf(length(bad_names), paste(bad_names, collapse = ", ")) %>%
+ stop()
}
}
diff --git a/README.md b/README.md
index 895bfb73..5fc21811 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[REDCapR](https://github.com/OuhscBbmc/REDCapR)
+[REDCapR](https://github.com/OuhscBbmc/REDCapR)
=======
We’ve been using R with [REDCap](https://projectredcap.org/)'s API since 2012 and have developed [`REDCapR`](https://github.com/OuhscBbmc/REDCapR). Before encapsulating these functions in a package, we were replicating 50+ lines of code to contact REDCap and robustly transform the returned [csv](https://en.wikipedia.org/wiki/Comma-separated_values) into an R `data.frame`; it took twice that much to implement batching. All this can be done in one call to [`redcap_read()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_read.html):
@@ -60,7 +60,7 @@ We'd like to thank the following developers for their [advice](https://github.co
Much of this package has been developed to support the needs of the following projects. We appreciate the support. (So far) the primary developers of REDCapR are the external evaluators for [Oklahoma's MIECHV](https://oklahoma.gov/health/health-education/children---family-health/family-support-and-prevention-service/miechv-program-federal-home-visiting-grant.html) program. See the preliminary CQI reports (many of which use REDCapR) at .
-* *OUHSC CCAN Independent Evaluation of the State of Oklahoma Competitive Maternal, Infant, and Early Childhood Home Visiting ([MIECHV](https://mchb.hrsa.gov/maternal-child-health-initiatives/home-visiting-overview)) Project*. HRSA/ACF D89MC23154. David Bard, PI, OUHSC; 2011-2015.
+* *OUHSC CCAN Independent Evaluation of the State of Oklahoma Competitive Maternal, Infant, and Early Childhood Home Visiting ([MIECHV](https://mchb.hrsa.gov/programs-impact/programs/home-visiting/maternal-infant-early-childhood-home-visiting-miechv-program)) Project*. HRSA/ACF D89MC23154. David Bard, PI, OUHSC; 2011-2015.
* *Independent Evaluation of the State of OK MIECHV Evidence Based Home Visitation Project*, [NIH](https://www.nih.gov/)-sponsored collaboration with [OSDH](https://oklahoma.gov/health.html). David Bard, PI, OUHSC; 2015-2017.
* *OSDH ParentPRO Pilot Evaluation*, federally-sponsored collaboration with [OSDH](https://oklahoma.gov/health.html). David Bard, PI, OUHSC; 2015-2017.
* *Title IV-E Waiver Project*, [HRSA/MCHB](https://mchb.hrsa.gov/)-sponsored collaboration with [OKDHS](https://oklahoma.gov/okdhs.html); David Bard, PI, OUHSC; 2014-2017.
diff --git a/_pkgdown.yml b/_pkgdown.yml
index e866cd89..f0ac4515 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -1,6 +1,7 @@
url: https://ouhscbbmc.github.io/REDCapR
template:
+ bootstrap: 5
params:
bootswatch: simplex
docsearch:
diff --git a/documentation-for-developers/r-medicine-2024-part-1.R b/documentation-for-developers/r-medicine-2024-part-1.R
new file mode 100644
index 00000000..1c1c5a91
--- /dev/null
+++ b/documentation-for-developers/r-medicine-2024-part-1.R
@@ -0,0 +1,101 @@
+## ----pre-req------------------------------------------------------------------
+requireNamespace("REDCapR")
+
+# If this fails, run `install.packages("REDCapR")` or `remotes::install_github(repo="OuhscBbmc/REDCapR")`
+
+## ----retrieve-credential------------------------------------------------------
+path_credential <- system.file("misc/example.credentials", package = "REDCapR")
+credential <- REDCapR::retrieve_credential_local(
+ path_credential = path_credential,
+ project_id = 3181
+)
+
+credential
+
+## ----unstructured-1-----------------------------------------------------------
+ds_1 <-
+ REDCapR::redcap_read(
+ redcap_uri = credential$redcap_uri,
+ token = credential$token
+ )$data
+
+View(ds_1)
+
+
+# View corresponding REDCap project in browser:
+# https://bbmc.ouhsc.edu/redcap/redcap_v14.3.13/DataEntry/record_home.php?pid=3181&arm=1&id=1
+
+
+# ---- redcap_metadata_read ----------------------------------------------------
+REDCapR::redcap_metadata_read(
+ redcap_uri = credential$redcap_uri,
+ token = credential$token
+)$data
+
+
+# ---- grain - intake -----------------------------------------------------------
+col_types_intake <-
+ readr::cols_only(
+ record_id = readr::col_integer(),
+ height = readr::col_double(),
+ weight = readr::col_double(),
+ bmi = readr::col_double()
+ )
+
+ds_intake <-
+ REDCapR::redcap_read(
+ redcap_uri = credential$redcap_uri, # From the previous code snippet.
+ token = credential$token,
+ forms = "intake",
+ col_types = col_types_intake,
+ verbose = FALSE,
+ )$data
+
+ds_intake
+
+
+# ---- grain - bp --------------------------------------------------------------
+col_types_blood_pressure <-
+ readr::cols(
+ record_id = readr::col_integer(),
+ redcap_repeat_instrument = readr::col_character(),
+ redcap_repeat_instance = readr::col_integer(),
+ sbp = readr::col_double(),
+ dbp = readr::col_double(),
+ blood_pressure_complete = readr::col_integer()
+ )
+
+ds_blood_pressure <-
+ REDCapR::redcap_read(
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ forms = "blood_pressure",
+ col_types = col_types_blood_pressure,
+ verbose = FALSE
+ )$data
+
+ds_blood_pressure |>
+ tidyr::drop_na(redcap_repeat_instrument)
+
+# ---- grain - labs ------------------------------------------------------------
+col_types_laboratory <-
+ readr::cols(
+ record_id = readr::col_integer(),
+ redcap_repeat_instrument = readr::col_character(),
+ redcap_repeat_instance = readr::col_integer(),
+ lab = readr::col_character(),
+ conc = readr::col_character(),
+ laboratory_complete = readr::col_integer()
+ )
+
+ds_laboratory <-
+ REDCapR::redcap_read(
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ forms = "laboratory",
+ col_types = col_types_laboratory,
+ verbose = FALSE
+ )$data
+
+ds_laboratory |>
+ tidyr::drop_na(redcap_repeat_instrument)
diff --git a/documentation-for-developers/r-medicine-2024-part-3.R b/documentation-for-developers/r-medicine-2024-part-3.R
new file mode 100644
index 00000000..b2836134
--- /dev/null
+++ b/documentation-for-developers/r-medicine-2024-part-3.R
@@ -0,0 +1,382 @@
+---
+title: "Writing to a REDCap Project"
+author: Will Beasley [Biomedical & Behavior Methodology Core](https://www.ouhsc.edu/bbmc/team/), OUHSC Pediatrics;;
Raymond Balise, University of Miami School of Medicine
Stephan Kadauke, Children's Hospital of Philadelphia
+output:
+ rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{Writing to a REDCap Project}
+ %\VignetteEngine{knitr::rmarkdown}
+ %\VignetteEncoding{UTF-8}
+---
+
+```{r}
+#| include = FALSE
+knitr::opts_chunk$set(
+ collapse = TRUE,
+ comment = "#>",
+ tidy = FALSE
+)
+```
+
+Writing data _to_ REDCap is more difficult than reading data _from_ REDCap.
+When you read, you receive data in the structure that the REDCap provides you.
+You have some control about the columns, rows, and data types,
+but there is not a lot you have to be concerned.
+
+In contrast, the structure of the dataset you send to the REDCap server must be precise.
+You need to pass special variables so that the REDCap server understands the
+hierarchical structure of the data points.
+This vignette walks you through that process.
+
+If you are new to REDCap and its API,
+please first understand the concepts described in these two [vignettes](https://ouhscbbmc.github.io/REDCapR/articles/):
+
+* [Typical REDCap Workflow for a Data Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
+* [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
+
+Part 1 - Intro
+===================================
+
+Strategy
+----------------------------------
+
+As described in the [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html) vignette,
+the best way to read and write data from projects with longitudinal/repeating elements
+is to break up the "block matrix" dataset into individual datasets.
+Each rectangle should have a coherent grain.
+
+Following this strategy, we'll write to the REDCap server in two distinct steps:
+
+1. Upload the patient-level instrument(s)
+1. Upload the each repeating instrument separately.
+
+The actual upload phase is pretty straight-forward
+--it's just a call to `REDCapR::redcap_write()`.
+Most of the vignette's code prepares the dataset so that the upload will run smoothly.
+
+Pre-requisites
+----------------------------------
+
+See the [Typical REDCap Workflow for a Data Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
+vignette and
+
+1. [Verify REDCapR is installed](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcapr-is-installed)
+1. [Verify REDCap Access](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcap-access)
+1. [Review Codebook](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#review-codebook)
+
+Retrieve Token
+-------------------------
+
+Please closely read the
+[Retrieve Protected Token](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#part-2---retrieve-protected-token) section,
+which has important security implications.
+The current vignette imports a fake dataset into REDCap,
+and we'll use a token stored in a local file.
+
+```r
+# retrieve-credential
+path_credential <- system.file("misc/example.credentials", package = "REDCapR")
+credential <- REDCapR::retrieve_credential_local(
+ path_credential = path_credential,
+ project_id = 3748
+)
+
+c(credential$redcap_uri, credential$token)
+```
+
+Datasets to Write to Server
+-------------------------
+
+To keep this vignette focused on writing/importing/uploading to the server,
+we'll start with the data that needs to be written.
+These example tables were prepared by [Raymond Balise](https://github.com/RaymondBalise)
+for our 2023 [R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
+"Using REDCap and R to Rapidly Produce Biomedical Publications".
+
+There are two tables, each with a different [granularity](https://www.1keydata.com/datawarehousing/fact-table-granularity.html):
+
+* `ds_patient`: each row represents one patient,
+* `ds_daily`: each row represents one daily measurement per patient.
+
+```r
+# load-patient
+ds_patient <-
+ "test-data/vignette-repeating-write/data-patient.rds" |>
+ system.file(package = "REDCapR") |>
+ readr::read_rds()
+
+ds_patient
+```
+
+```r
+# load-repeating
+ds_daily <-
+ "test-data/vignette-repeating-write/data-daily.rds" |>
+ system.file(package = "REDCapR") |>
+ readr::read_rds()
+
+ds_daily
+```
+
+Part 2 - Write Data: One row per patient
+===================================
+
+Besides the [`data.frame`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/data.frame.html)
+to write to REDCap,
+the only required arguments of the
+[`REDCapR::redcap_write()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_write.html)
+function are `redcap_uri` and `token`;
+both are contained in the credential object created in the previous section.
+
+As discussed in the [Troubleshooting vignette](https://ouhscbbmc.github.io/REDCapR/articles/TroubleshootingApiCalls.html#writing),
+we recommend running these two preliminary checks before trying to write the
+dataset to the server for the very first time.
+
+Prep: Stoplight Fields
+-------------------------
+
+If the REDCap project isn't longitudinal and doesn't have arms,
+uploading a patient-level data.frame to REDCap doesn't require adding variables.
+However we typically populate the `*_complete` variables to communicate the record's status.
+
+If the row is needs a human to add more values or inspect the existing values
+consider [marking the instrument](https://ouhscbbmc.github.io/REDCapR/reference/constant.html)
+"incomplete" or "unverified";
+the patient's instrument record will appear red or yellow in REDCap's Record Dashboard.
+Otherwise consider marking the instrument "complete" so
+it will appear green.
+
+With this example project, the only patient-level instrument is "enrollment",
+so the corresponding variable is `enrollment_complete`.
+
+```r
+# patient-complete
+ds_patient <-
+ ds_patient |>
+ dplyr::mutate(
+ enrollment_complete = REDCapR::constant("form_complete"),
+ )
+```
+
+Prep: `REDCapR::validate_for_write()`
+-------------------------
+
+`REDCapR::validate_for_write()` inspects a data frame to anticipate potential problems before writing with REDCap's API.
+A tibble is returned, with one row per potential problem (and a suggestion how to avoid it).
+Ideally an 0-row tibble is returned.
+
+```r
+REDCapR::validate_for_write(ds_patient, convert_logical_to_integer = TRUE)
+```
+
+If you encounter problems that can be checked with automation,
+please tell us in [an issue](https://github.com/OuhscBbmc/REDCapR/issues).
+We'll work with you to incorporate the new check into `REDCapR::validate_for_write()`.
+
+When a dataset's problems are caught before reaching the server,
+the solutions are easier to identify and implement.
+
+Prep: Write Small Subset First
+-------------------------
+
+If this is your first time with a complicated project, consider loading a small subset of rows and columns.
+In this case, we start with only three columns and two rows.
+
+```r
+# patient-subset
+ds_patient |>
+ dplyr::select( # First three columns
+ id_code,
+ date,
+ is_mobile,
+ ) |>
+ dplyr::slice(1:2) |> # First two rows
+ REDCapR::redcap_write(
+ ds_to_write = _,
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ convert_logical_to_integer = TRUE
+ )
+```
+
+Prep: Recode Variables where Necessary
+-------------------------
+
+Some variables in the data.frame might be represented differently than in REDCap.
+
+A common transformation is changing strings into the integers that underlie radio buttons.
+Common approaches are [`dplyr::case_match()`](https://dplyr.tidyverse.org/reference/case_match.html) and
+using joining to lookup tables (if the mappings are expressed in a csv).
+Here's an in-line example of `dplyr::case_match()`.
+
+```r
+ds_patient <-
+ ds_patient |>
+ dplyr::mutate(
+ race =
+ dplyr::case_match(
+ race,
+ "White" ~ 1L,
+ "Black or African American" ~ 2L,
+ "Asian" ~ 3L,
+ "Native American" ~ 4L,
+ "Pacific Islander" ~ 5L,
+ "Multiracial" ~ 6L,
+ "Refused or don't know" ~ 7L
+ )
+ )
+```
+
+```{r codebook-race}
+#| echo = FALSE,
+#| out.extra = 'style = "fig.width=1200px"'
+knitr::include_graphics("images/codebook-race.png")
+```
+
+Write Entire Patient-level Table
+-------------------------
+
+If the small subset works, we usually jump ahead and try all columns and rows.
+
+If this larger table fails, split the difference between
+(a) the smaller working example and
+(b) the larger failing example.
+See if this middle point (that has fewer rows and/or columns than the failing point)
+succeeds or fails.
+Then repeat.
+This "bisection" or "binary search" [debugging technique](https://medium.com/codecastpublication/debugging-tools-and-techniques-binary-search-2da5bb4282c7) is helpful in many areas of programming and statistical modeling.
+
+```r
+# patient-entire
+ds_patient |>
+ REDCapR::redcap_write(
+ ds_to_write = _,
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ convert_logical_to_integer = TRUE
+ )
+```
+
+Part 3 - Write Data: Repeating Instrument
+===================================
+
+Add Plumbing Variables
+-------------------------
+
+As stated in the vignette's intro,
+the structure of the dataset uploaded to the server must be precise.
+When uploading repeating instruments, there are several important columns:
+
+1. `record_id`: typically indicates the patient's id. (This field can be renamed for the project.)
+1. `redcap_event_name`: If the project is longitudinal or has arms, this indicates the event.
+ Otherwise, you don't need to add this variable.
+1. `redcap_repeat_instrument`: Indicates the instrument/form that is repeating for these columns.
+1. `redcap_repeat_instance`: Typically a sequential positive integer (*e.g.*, 1, 2, 3, ...) indicating the order.
+
+The combination of these variables needs to be unique.
+Please read the [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
+vignette for details of these variables and their meanings.
+
+You need to pass specific variables so that the REDCap server understands the hierarchical structure of the data points.
+
+```r
+# repeat-plumbing
+ds_daily <-
+ ds_daily |>
+ dplyr::group_by(id_code) |>
+ dplyr::mutate(
+ redcap_repeat_instrument = "daily",
+ redcap_repeat_instance = dplyr::row_number(da_date),
+ daily_complete = REDCapR::constant("form_complete"),
+ ) |>
+ dplyr::ungroup() |>
+ dplyr::select(
+ id_code, # Or `record_id`, if you didn't rename it
+ # redcap_event_name, # If the project is longitudinal or has arms
+ redcap_repeat_instrument, # The name of the repeating instrument/form
+ redcap_repeat_instance, # The sequence of the repeating instrument
+ tidyselect::everything(), # All columns not explicitly passed to `dplyr::select()`
+ daily_complete, # Indicates incomplete, unverified, or complete
+ )
+
+# Check for potential problems. (Remember zero rows are good.)
+REDCapR::validate_for_write(ds_daily, convert_logical_to_integer = TRUE)
+
+ds_daily
+```
+
+Writing Repeating Instrument Variables
+-------------------------
+
+```r
+# daily-entire
+ds_daily |>
+ REDCapR::redcap_write(
+ ds_to_write = _,
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ convert_logical_to_integer = TRUE
+ )
+```
+
+Part 4 - Next Steps
+===================================
+
+More Complexity
+-------------------------
+
+This vignette required only two data.frames, but more complex projects sometimes need more.
+For example, each repeating instrument should be its own data.frame and
+writing step. Arms and longitudinal events need to be considered too.
+
+Batching
+-------------------------
+
+By default, `REDCapR::redcap_write()` requests datasets of 100 patients as a time,
+and stacks the resulting subsets together before returning a data.frame.
+This can be adjusted to improve performance;
+the 'Details' section of `REDCapR::redcap_write()` discusses the trade offs.
+
+I usually shoot for ~10 seconds per batch.
+
+Manual vs API
+-------------------------
+
+Manual downloading/uploading might make sense if you're do the operation only once.
+But when does it ever stop after the first time?
+
+If you have trouble uploading, consider adding a few fake patients & measurements
+and then download the csv.
+It might reveal something you didn't anticipate.
+But be aware that it will be in the block matrix format
+(*i.e.*, everything jammed into one rectangle.)
+
+Notes
+===================================
+
+This vignette was originally designed for the
+[2023 R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
+_Using REDCap and R to Rapidly Produce Biomedical Publications Cleaning Medical Data_
+with [Raymond R. Balise](https://github.com/RaymondBalise), Belén Hervera, Daniel Maya, Anna Calderon, Tyler Bartholomew, Stephan Kadauke, and João Pedro Carmezim Correia and the [2024 R/Medicine](https://rconsortium.github.io/RMedicine_website/Program.html) workshop,
+_REDCap + R: Teaming Up in the Tidyverse_, with Stephan Kadauke.
+The workshop slides are for [2023](https://github.com/RaymondBalise/r_med_redcap_2023_public)
+and [2024](https://github.com/skadauke/rmedicine_2024_redcap_r_workshop).
+
+This work was made possible in part by the NIH grant [U54GM104938](https://taggs.hhs.gov/Detail/AwardDetail?arg_AwardNum=U54GM104938&arg_ProgOfficeCode=127)
+to the [Oklahoma Shared Clinical and Translational Resource)](http://osctr.ouhsc.edu).
+
+Session Information
+==================================================================
+
+For the sake of documentation and reproducibility, the current report was rendered in the following environment. Click the line below to expand.
+
+
+ Environment
+```{r session-info, echo=FALSE}
+if (requireNamespace("sessioninfo", quietly = TRUE)) {
+ sessioninfo::session_info()
+} else {
+ sessionInfo()
+}
+```
+
diff --git a/inst/WORDLIST b/inst/WORDLIST
index d5aa325f..46aa9c30 100644
--- a/inst/WORDLIST
+++ b/inst/WORDLIST
@@ -4,11 +4,13 @@ Barbone
Belén
CAPture
CCAN
+CDIS
CDS
CMD
CQI
CRAN's
Carmezim
+Cerner
Codebook
Codecov
Correia
@@ -20,9 +22,11 @@ DSN's
De
Dev
EAV
+EHR
EMR
EMRs
EMs
+FHIR
Funders
Gabor
HRSA
@@ -65,7 +69,7 @@ RODBCext
Recode
SSL
SecurityDatabase
-Sys
+Supertibble
Tidyverse
Timezones
Translational
@@ -125,7 +129,6 @@ nonmissing
nonnull
odbc
ouhscbbmc
-packageVersion
perl
phpcap
pid
@@ -134,7 +137,6 @@ programmatically
projectredcap
pseudofield
pseudofields
-qqq
rdoc
readr
readr's
@@ -143,7 +145,6 @@ regexes
reproducibility
sbp
schemas
-setenv
supertibble
testthat
tibble
diff --git a/inst/test-data/specific-redcapr/users-export/with-dags--user.R b/inst/test-data/specific-redcapr/users-export/with-dags--user.R
index 67fc4276..d4bfdace 100644
--- a/inst/test-data/specific-redcapr/users-export/with-dags--user.R
+++ b/inst/test-data/specific-redcapr/users-export/with-dags--user.R
@@ -8,13 +8,13 @@ structure(list(username = c("unittestphifree", "wbeasleya"),
), stats_and_charts = c(FALSE, TRUE), manage_survey_participants = c(TRUE,
TRUE), calendar = c(FALSE, TRUE), data_import_tool = c(FALSE,
TRUE), data_comparison_tool = c(FALSE, TRUE), logging = c(FALSE,
- TRUE), file_repository = c(FALSE, TRUE), data_quality_create = c(FALSE,
- TRUE), data_quality_execute = c(FALSE, TRUE), api_export = c(TRUE,
- TRUE), api_import = c(FALSE, TRUE), api_modules = c(0, 0),
- mobile_app = c(FALSE, TRUE), mobile_app_download_data = c(FALSE,
- TRUE), record_create = c(FALSE, TRUE), record_rename = c(FALSE,
- FALSE), record_delete = c(FALSE, FALSE), lock_records_all_forms = c(FALSE,
- FALSE), lock_records = c(FALSE, FALSE), lock_records_customization = c(FALSE,
- FALSE), forms_export = c("demographics:1", "demographics:1"
- )), row.names = c(NA, -2L), class = c("tbl_df", "tbl", "data.frame"
-))
+ TRUE), email_logging = c(FALSE, TRUE), file_repository = c(FALSE,
+ TRUE), data_quality_create = c(FALSE, TRUE), data_quality_execute = c(FALSE,
+ TRUE), api_export = c(TRUE, TRUE), api_import = c(FALSE,
+ TRUE), api_modules = c(FALSE, FALSE), mobile_app = c(FALSE,
+ TRUE), mobile_app_download_data = c(FALSE, TRUE), record_create = c(FALSE,
+ TRUE), record_rename = c(FALSE, FALSE), record_delete = c(FALSE,
+ FALSE), lock_records_all_forms = c(FALSE, FALSE), lock_records = c(FALSE,
+ FALSE), lock_records_customization = c(FALSE, FALSE), forms_export = c("demographics:1",
+ "demographics:1")), row.names = c(NA, -2L), class = c("tbl_df",
+"tbl", "data.frame"))
diff --git a/inst/test-data/specific-redcapr/users-export/without-dags--user.R b/inst/test-data/specific-redcapr/users-export/without-dags--user.R
index 28c53b0c..4f0bf417 100644
--- a/inst/test-data/specific-redcapr/users-export/without-dags--user.R
+++ b/inst/test-data/specific-redcapr/users-export/without-dags--user.R
@@ -9,13 +9,13 @@ structure(list(username = c("unittestphifree", "wbeasleya"),
), stats_and_charts = c(TRUE, TRUE), manage_survey_participants = c(TRUE,
TRUE), calendar = c(TRUE, TRUE), data_import_tool = c(FALSE,
TRUE), data_comparison_tool = c(FALSE, TRUE), logging = c(TRUE,
- TRUE), file_repository = c(TRUE, TRUE), data_quality_create = c(FALSE,
- TRUE), data_quality_execute = c(FALSE, TRUE), api_export = c(TRUE,
- FALSE), api_import = c(FALSE, FALSE), api_modules = c(0,
- 0), mobile_app = c(FALSE, FALSE), mobile_app_download_data = c(FALSE,
- FALSE), record_create = c(TRUE, TRUE), record_rename = c(FALSE,
- FALSE), record_delete = c(FALSE, FALSE), lock_records_all_forms = c(FALSE,
- FALSE), lock_records = c(FALSE, FALSE), lock_records_customization = c(FALSE,
- FALSE), forms_export = c("demographics:1,health:1,race_and_ethnicity:1",
+ TRUE), email_logging = c(FALSE, TRUE), file_repository = c(TRUE,
+ TRUE), data_quality_create = c(FALSE, TRUE), data_quality_execute = c(FALSE,
+ TRUE), api_export = c(TRUE, FALSE), api_import = c(FALSE,
+ FALSE), api_modules = c(FALSE, FALSE), mobile_app = c(FALSE,
+ FALSE), mobile_app_download_data = c(FALSE, FALSE), record_create = c(TRUE,
+ TRUE), record_rename = c(FALSE, FALSE), record_delete = c(FALSE,
+ FALSE), lock_records_all_forms = c(FALSE, FALSE), lock_records = c(FALSE,
+ FALSE), lock_records_customization = c(FALSE, FALSE), forms_export = c("demographics:1,health:1,race_and_ethnicity:1",
"demographics:1,health:1,race_and_ethnicity:1")), row.names = c(NA,
-2L), class = c("tbl_df", "tbl", "data.frame"))
diff --git a/man/REDCapR-package.Rd b/man/REDCapR-package.Rd
index f94098a7..9596baf2 100644
--- a/man/REDCapR-package.Rd
+++ b/man/REDCapR-package.Rd
@@ -18,7 +18,7 @@ following projects. We appreciate the support.
\itemize{
\item \emph{OUHSC CCAN Independent Evaluation of the State of Oklahoma Competitive
Maternal, Infant, and Early Childhood Home Visiting
-(\href{https://mchb.hrsa.gov/maternal-child-health-initiatives/home-visiting-overview}{MIECHV}) Project}.
+(\href{https://mchb.hrsa.gov/programs-impact/programs/home-visiting/maternal-infant-early-childhood-home-visiting-miechv-program}{MIECHV}) Project}.
HRSA/ACF D89MC23154.
David Bard, PI, OUHSC; 2011-2015.
\item \emph{Independent Evaluation of the State of OK MIECHV Evidence Based Home
diff --git a/man/redcap_survey_link_export_oneshot.Rd b/man/redcap_survey_link_export_oneshot.Rd
index b4d36f66..17eef526 100644
--- a/man/redcap_survey_link_export_oneshot.Rd
+++ b/man/redcap_survey_link_export_oneshot.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/redcap-survey-link-export-oneshot.R
\name{redcap_survey_link_export_oneshot}
\alias{redcap_survey_link_export_oneshot}
-\title{Download a file from a REDCap project record}
+\title{Get survey link from REDCap}
\usage{
redcap_survey_link_export_oneshot(
redcap_uri,
@@ -25,12 +25,14 @@ Required.}
\item{token}{The user-specific string that serves as the password for a
project. Required.}
-\item{record}{The record ID where the file is to be imported. Required}
+\item{record}{The record ID associated with the
+survey link. Required}
\item{instrument}{The name of the instrument associated with the
survey link. Required}
-\item{event}{The name of the event where the file is saved in REDCap.
+\item{event}{The name of the event associated with the
+survey link.
Optional}
\item{verbose}{A boolean value indicating if \code{message}s should be printed
@@ -47,6 +49,7 @@ should be \code{NULL} for most institutions. Optional.}
\value{
Currently, a list is returned with the following elements,
\itemize{
+\item \code{survey_link}: a character string containing the URL for the survey.
\item \code{success}: A boolean value indicating if the operation was apparently
successful.
\item \code{status_code}: The
@@ -54,18 +57,18 @@ successful.
of the operation.
\item \code{outcome_message}: A human readable string indicating the operation's
outcome.
-\item \code{records_affected_count}: The number of records inserted or updated.
-\item \code{affected_ids}: The subject IDs of the inserted or updated records.
+\item \code{instrument}: The instrument associated with the survey link.
+\item \code{records_affected_count}: The number of records associated with
+the survey link.
+\item \code{affected_ids}: The subject IDs associated with the survey link.
\item \code{elapsed_seconds}: The duration of the function.
\item \code{raw_text}: If an operation is NOT successful, the text returned by
REDCap. If an operation is successful, the \code{raw_text} is returned as an
empty string to save RAM.
-\item \code{file_name}: The name of the file persisted to disk. This is useful if
-the name stored in REDCap is used (which is the default).
}
}
\description{
-This function uses REDCap's API to download a file.
+This function uses REDCap's API to get the link for a survey.
}
\details{
Currently, the function doesn't modify any variable types to conform to
diff --git a/tests/manual/test-could-not-connect-rate.R b/tests/manual/test-could-not-connect-rate.R
index 98356f48..0ea5d6e0 100644
--- a/tests/manual/test-could-not-connect-rate.R
+++ b/tests/manual/test-could-not-connect-rate.R
@@ -43,7 +43,7 @@ for (i in seq_len(file_read_count)) {
)
expect_true(file.exists(returned_object$file_name), "The downloaded file should exist.")
- }, finally = base::unlink("mugshot-1.jpg")
+ }, finally = base::unlink("mugshot-1.jpg")
)
message(i, ": ", returned_object$elapsed_seconds, " -", returned_object$raw_text)
diff --git a/tests/testthat/test-metadata-utilities.R b/tests/testthat/test-metadata-utilities.R
index 6ceed0f3..6395bd34 100644
--- a/tests/testthat/test-metadata-utilities.R
+++ b/tests/testthat/test-metadata-utilities.R
@@ -182,7 +182,7 @@ test_that("checkbox choices with errant space", {
"3", "Personality disorder",
"4", "Anxiety",
"0", "Not Noted"
- )
+ )
"1, Depressive mood disorder | 2, Adjustment disorder| 3, Personality disorder | 4, Anxiety | 0, Not Noted" |>
checkbox_choices() |> # datapasta::tribble_paste()
diff --git a/tests/testthat/test-read-oneshot-eav.R b/tests/testthat/test-read-oneshot-eav.R
index 44feed72..1d06c729 100644
--- a/tests/testthat/test-read-oneshot-eav.R
+++ b/tests/testthat/test-read-oneshot-eav.R
@@ -134,7 +134,7 @@ test_that("label-and-dag", {
raw_or_label = "label",
export_data_access_groups = TRUE,
verbose = FALSE
- )
+ )
if (update_expectation) save_expected(returned_object$data, path_expected)
expected_data_frame <- retrieve_expected(path_expected)
diff --git a/tests/testthat/test-read-russian.R b/tests/testthat/test-read-russian.R
index e8dc4440..fd5d2f21 100644
--- a/tests/testthat/test-read-russian.R
+++ b/tests/testthat/test-read-russian.R
@@ -19,8 +19,8 @@ test_that("Russian Recruit", {
# Sys.getlocale()
# Experiment w/ Joe Cheng's answer at https://stackoverflow.com/questions/5031630/how-to-source-r-file-saved-using-utf-8-encoding
-# expect_equal(d$recruitment_other[1], expected_single)
-# expect_equal(d$recruitment_other, expected_multiple)
+ # expect_equal(d$recruitment_other[1], expected_single)
+ # expect_equal(d$recruitment_other, expected_multiple)
})
# test_that("Russian Encoded", {
diff --git a/tests/testthat/test-survey-link-export-oneshot.R b/tests/testthat/test-survey-link-export-oneshot.R
index 4171d6f6..72d09846 100644
--- a/tests/testthat/test-survey-link-export-oneshot.R
+++ b/tests/testthat/test-survey-link-export-oneshot.R
@@ -60,7 +60,7 @@ test_that("Nonexistent Record ID", {
expect_equal(result$instrument, "participant_morale_questionnaire")
expect_equal(result$records_affected_count, 0L)
expect_equal(result$affected_ids, character(0))
- expect_equal(result$raw_text, "ERROR: The record '-1' does not exist")
+ expect_equal(result$raw_text, "ERROR: The record \"-1\" does not exist")
})
rm(credential, record, instrument)
diff --git a/tests/testthat/test-validate-no-logical.R b/tests/testthat/test-validate-no-logical.R
index 1d80ecef..e5b80bfd 100644
--- a/tests/testthat/test-validate-no-logical.R
+++ b/tests/testthat/test-validate-no-logical.R
@@ -37,7 +37,7 @@ test_that("validate_no_logical -concern dataset", {
# )
test_that("repeat-instance: no column", {
- ds <- validate_repeat_instance(mtcars)
+ ds <- validate_repeat_instance(mtcars)
expect_equal(object = nrow(ds), expected = 0)
})
diff --git a/tests/testthat/test-validate-repeat.R b/tests/testthat/test-validate-repeat.R
index 1f4c5830..557cd23e 100644
--- a/tests/testthat/test-validate-repeat.R
+++ b/tests/testthat/test-validate-repeat.R
@@ -9,7 +9,7 @@ test_that("validate_repeat_instance: good", {
d <-
mtcars %>%
dplyr::mutate(
- redcap_repeat_instance = sample(1:100, size = 32, replace = TRUE)
+ redcap_repeat_instance = sample(1:100, size = 32, replace = TRUE)
)
ds <- validate_repeat_instance(d)
expect_equal(object = nrow(ds), expected = 0)
diff --git a/tests/testthat/test-variables.R b/tests/testthat/test-variables.R
index 393bc5d1..74ab099c 100644
--- a/tests/testthat/test-variables.R
+++ b/tests/testthat/test-variables.R
@@ -58,13 +58,13 @@ test_that("Bad Uri -wrong address (2 of 2)", {
# Windows gives a different message than Travis/Linux
expected_outcome_message <- "(https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com|Couldn't resolve host 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com')"
# "The REDCapR variable retrieval was not successful\\..+?Error 405 \\(Method Not Allowed\\).+"
- # expected_outcome_message <- "(?s)The REDCapR variable retrieval was not successful\\..+?.+"
+ # expected_outcome_message <- "(?s)The REDCapR variable retrieval was not successful\\..+?.+"
expect_error(
redcap_variables(
redcap_uri = bad_uri,
token = credential$token
- )#,
+ )#,
# regexp = expected_outcome_message
)
diff --git a/tests/testthat/test-version.R b/tests/testthat/test-version.R
index 24389ef4..ce0c5625 100644
--- a/tests/testthat/test-version.R
+++ b/tests/testthat/test-version.R
@@ -19,10 +19,10 @@ test_that("version-successful", {
testthat::skip_on_cran()
actual <-
redcap_version(
- redcap_uri = credential$redcap_uri,
- token = credential$token,
- verbose = FALSE
- )
+ redcap_uri = credential$redcap_uri,
+ token = credential$token,
+ verbose = FALSE
+ )
expected <- package_version("13.7.1")
version_good <- (expected <= actual)
diff --git a/vignettes/advanced-redcapr-operations.Rmd b/vignettes/advanced-redcapr-operations.Rmd
index d1813a2c..5ba58c80 100644
--- a/vignettes/advanced-redcapr-operations.Rmd
+++ b/vignettes/advanced-redcapr-operations.Rmd
@@ -96,8 +96,8 @@ ds_wide <-
arm = as.integer(sub(pattern, "\\2", redcap_event_name))
) %>%
dplyr::select(study_id, event, arm, pmq1, pmq2, pmq3, pmq4) %>%
- dplyr::filter(!(event %in% c(
- "enrollment", "final_visit", "deadline_to_return", "deadline_to_opt_ou")
+ dplyr::filter(!(event %in%
+ c("enrollment", "final_visit", "deadline_to_return", "deadline_to_opt_ou")
)) %>%
tidyr::pivot_wider(
id_cols = c(study_id, arm),
@@ -147,8 +147,8 @@ ds_wide_2 <-
names_from = key,
values_from = value
)
- # For old versions of tidyr that predate `pivot_wider()`:
- # tidyr::spread(key=key, value=value)
+# For old versions of tidyr that predate `pivot_wider()`:
+# tidyr::spread(key=key, value=value)
ds_wide_2
```
diff --git a/vignettes/workflow-write.Rmd b/vignettes/workflow-write.Rmd
index 1a3b3c0c..f8985956 100644
--- a/vignettes/workflow-write.Rmd
+++ b/vignettes/workflow-write.Rmd
@@ -1,12 +1,15 @@
---
title: "Writing to a REDCap Project"
-author: Will Beasley [Biomedical & Behavior Methodology Core](https://www.ouhsc.edu/bbmc/team/), OUHSC Pediatrics;
Raymond Balise, University of Miami School of Medicine
+author: Will Beasley [Biomedical & Behavior Methodology Core](https://www.ouhsc.edu/bbmc/team/), OUHSC Pediatrics;;
Raymond Balise, University of Miami School of Medicine
Stephan Kadauke, Children's Hospital of Philadelphia
output:
rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Writing to a REDCap Project}
- %\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
+ %\VignetteEngine{knitr::rmarkdown}
+editor_options:
+ markdown:
+ wrap: 72
---
```{r}
@@ -18,62 +21,68 @@ knitr::opts_chunk$set(
)
```
-Writing data _to_ REDCap is more difficult than reading data _from_ REDCap.
-When you read, you receive data in the structure that the REDCap provides you.
-You have some control about the columns, rows, and data types,
-but there is not a lot you have to be concerned.
+Writing data *to* REDCap is more difficult than reading data *from*
+REDCap. When you read, you receive data in the structure that the REDCap
+provides you. You have some control about the columns, rows, and data
+types, but there is not a lot you have to be concerned.
-In contrast, the structure of the dataset you send to the REDCap server must be precise.
-You need to pass special variables so that the REDCap server understands the
-hierarchical structure of the data points.
-This vignette walks you through that process.
+In contrast, the structure of the dataset you send to the REDCap server
+must be precise. You need to pass special variables so that the REDCap
+server understands the hierarchical structure of the data points. This
+vignette walks you through that process.
-If you are new to REDCap and its API,
-please first understand the concepts described in these two [vignettes](https://ouhscbbmc.github.io/REDCapR/articles/):
+If you are new to REDCap and its API, please first understand the
+concepts described in these two
+[vignettes](https://ouhscbbmc.github.io/REDCapR/articles/):
-* [Typical REDCap Workflow for a Data Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
-* [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
+- [Typical REDCap Workflow for a Data
+ Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
+- [Retrieving Longitudinal and Repeating
+ Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
-Part 1 - Intro
-===================================
+# Part 1 - Intro
-Strategy
-----------------------------------
+## Strategy
-As described in the [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html) vignette,
-the best way to read and write data from projects with longitudinal/repeating elements
-is to break up the "block matrix" dataset into individual datasets.
-Each rectangle should have a coherent grain.
+As described in the [Retrieving Longitudinal and Repeating
+Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
+vignette, the best way to read and write data from projects with
+longitudinal/repeating elements is to break up the "block matrix"
+dataset into individual datasets. Each rectangle should have a coherent
+grain.
-Following this strategy, we'll write to the REDCap server in two distinct steps:
+Following this strategy, we'll write to the REDCap server in two
+distinct steps:
-1. Upload the patient-level instrument(s)
-1. Upload the each repeating instrument separately.
+1. Upload the patient-level instrument(s)
+2. Upload the each repeating instrument separately.
-The actual upload phase is pretty straight-forward
---it's just a call to `REDCapR::redcap_write()`.
-Most of the vignette's code prepares the dataset so that the upload will run smoothly.
+The actual upload phase is pretty straight-forward --it's just a call to
+`REDCapR::redcap_write()`. Most of the vignette's code prepares the
+dataset so that the upload will run smoothly.
-Pre-requisites
-----------------------------------
+## Pre-requisites
-See the [Typical REDCap Workflow for a Data Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
+See the [Typical REDCap Workflow for a Data
+Analyst](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html)
vignette and
-1. [Verify REDCapR is installed](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcapr-is-installed)
-1. [Verify REDCap Access](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcap-access)
-1. [Review Codebook](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#review-codebook)
+1. [Verify REDCapR is
+ installed](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcapr-is-installed)
+2. [Verify REDCap
+ Access](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#verify-redcap-access)
+3. [Review
+ Codebook](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#review-codebook)
-Retrieve Token
--------------------------
+## Retrieve Token
-Please closely read the
-[Retrieve Protected Token](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#part-2---retrieve-protected-token) section,
-which has important security implications.
-The current vignette imports a fake dataset into REDCap,
-and we'll use a token stored in a local file.
+Please closely read the [Retrieve Protected
+Token](https://ouhscbbmc.github.io/REDCapR/articles/workflow-read.html#part-2---retrieve-protected-token)
+section, which has important security implications. The current vignette
+imports a fake dataset into REDCap, and we'll use a token stored in a
+local file.
-```r
+``` r
# retrieve-credential
path_credential <- system.file("misc/example.credentials", package = "REDCapR")
credential <- REDCapR::retrieve_credential_local(
@@ -84,21 +93,22 @@ credential <- REDCapR::retrieve_credential_local(
c(credential$redcap_uri, credential$token)
```
-Datasets to Write to Server
--------------------------
+## Datasets to Write to Server
-To keep this vignette focused on writing/importing/uploading to the server,
-we'll start with the data that needs to be written.
-These example tables were prepared by [Raymond Balise](https://github.com/RaymondBalise)
-for our 2023 [R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
+To keep this vignette focused on writing/importing/uploading to the
+server, we'll start with the data that needs to be written. These
+example tables were prepared by [Raymond
+Balise](https://github.com/RaymondBalise) for our 2023
+[R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
"Using REDCap and R to Rapidly Produce Biomedical Publications".
-There are two tables, each with a different [granularity](https://www.1keydata.com/datawarehousing/fact-table-granularity.html):
+There are two tables, each with a different
+[granularity](https://www.1keydata.com/datawarehousing/fact-table-granularity.html):
-* `ds_patient`: each row represents one patient,
-* `ds_daily`: each row represents one daily measurement per patient.
+- `ds_patient`: each row represents one patient,
+- `ds_daily`: each row represents one daily measurement per patient.
-```r
+``` r
# load-patient
ds_patient <-
"test-data/vignette-repeating-write/data-patient.rds" |>
@@ -108,7 +118,7 @@ ds_patient <-
ds_patient
```
-```r
+``` r
# load-repeating
ds_daily <-
"test-data/vignette-repeating-write/data-daily.rds" |>
@@ -118,38 +128,38 @@ ds_daily <-
ds_daily
```
-Part 2 - Write Data: One row per patient
-===================================
+# Part 2 - Write Data: One row per patient
-Besides the [`data.frame`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/data.frame.html)
-to write to REDCap,
-the only required arguments of the
+Besides the
+[`data.frame`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/data.frame.html)
+to write to REDCap, the only required arguments of the
[`REDCapR::redcap_write()`](https://ouhscbbmc.github.io/REDCapR/reference/redcap_write.html)
-function are `redcap_uri` and `token`;
-both are contained in the credential object created in the previous section.
+function are `redcap_uri` and `token`; both are contained in the
+credential object created in the previous section.
-As discussed in the [Troubleshooting vignette](https://ouhscbbmc.github.io/REDCapR/articles/TroubleshootingApiCalls.html#writing),
-we recommend running these two preliminary checks before trying to write the
-dataset to the server for the very first time.
+As discussed in the [Troubleshooting
+vignette](https://ouhscbbmc.github.io/REDCapR/articles/TroubleshootingApiCalls.html#writing),
+we recommend running these two preliminary checks before trying to write
+the dataset to the server for the very first time.
-Prep: Stoplight Fields
--------------------------
+## Prep: Stoplight Fields
If the REDCap project isn't longitudinal and doesn't have arms,
-uploading a patient-level data.frame to REDCap doesn't require adding variables.
-However we typically populate the `*_complete` variables to communicate the record's status.
+uploading a patient-level data.frame to REDCap doesn't require adding
+variables. However we typically populate the `*_complete` variables to
+communicate the record's status.
-If the row is needs a human to add more values or inspect the existing values
-consider [marking the instrument](https://ouhscbbmc.github.io/REDCapR/reference/constant.html)
-"incomplete" or "unverified";
-the patient's instrument record will appear red or yellow in REDCap's Record Dashboard.
-Otherwise consider marking the instrument "complete" so
-it will appear green.
+If the row is needs a human to add more values or inspect the existing
+values consider [marking the
+instrument](https://ouhscbbmc.github.io/REDCapR/reference/constant.html)
+"incomplete" or "unverified"; the patient's instrument record will
+appear red or yellow in REDCap's Record Dashboard. Otherwise consider
+marking the instrument "complete" so it will appear green.
-With this example project, the only patient-level instrument is "enrollment",
-so the corresponding variable is `enrollment_complete`.
+With this example project, the only patient-level instrument is
+"enrollment", so the corresponding variable is `enrollment_complete`.
-```r
+``` r
# patient-complete
ds_patient <-
ds_patient |>
@@ -158,31 +168,32 @@ ds_patient <-
)
```
-Prep: `REDCapR::validate_for_write()`
--------------------------
+## Prep: `REDCapR::validate_for_write()`
-`REDCapR::validate_for_write()` inspects a data frame to anticipate potential problems before writing with REDCap's API.
-A tibble is returned, with one row per potential problem (and a suggestion how to avoid it).
-Ideally an 0-row tibble is returned.
+`REDCapR::validate_for_write()` inspects a data frame to anticipate
+potential problems before writing with REDCap's API. A tibble is
+returned, with one row per potential problem (and a suggestion how to
+avoid it). Ideally an 0-row tibble is returned.
-```r
+``` r
REDCapR::validate_for_write(ds_patient, convert_logical_to_integer = TRUE)
```
-If you encounter problems that can be checked with automation,
-please tell us in [an issue](https://github.com/OuhscBbmc/REDCapR/issues).
-We'll work with you to incorporate the new check into `REDCapR::validate_for_write()`.
+If you encounter problems that can be checked with automation, please
+tell us in [an issue](https://github.com/OuhscBbmc/REDCapR/issues).
+We'll work with you to incorporate the new check into
+`REDCapR::validate_for_write()`.
-When a dataset's problems are caught before reaching the server,
-the solutions are easier to identify and implement.
+When a dataset's problems are caught before reaching the server, the
+solutions are easier to identify and implement.
-Prep: Write Small Subset First
--------------------------
+## Prep: Write Small Subset First
-If this is your first time with a complicated project, consider loading a small subset of rows and columns.
-In this case, we start with only three columns and two rows.
+If this is your first time with a complicated project, consider loading
+a small subset of rows and columns. In this case, we start with only
+three columns and two rows.
-```r
+``` r
# patient-subset
ds_patient |>
dplyr::select( # First three columns
@@ -199,17 +210,18 @@ ds_patient |>
)
```
-Prep: Recode Variables where Necessary
--------------------------
+## Prep: Recode Variables where Necessary
-Some variables in the data.frame might be represented differently than in REDCap.
+Some variables in the data.frame might be represented differently than
+in REDCap.
-A common transformation is changing strings into the integers that underlie radio buttons.
-Common approaches are [`dplyr::case_match()`](https://dplyr.tidyverse.org/reference/case_match.html) and
-using joining to lookup tables (if the mappings are expressed in a csv).
-Here's an in-line example of `dplyr::case_match()`.
+A common transformation is changing strings into the integers that
+underlie radio buttons. Common approaches are
+[`dplyr::case_match()`](https://dplyr.tidyverse.org/reference/case_match.html)
+and using joining to lookup tables (if the mappings are expressed in a
+csv). Here's an in-line example of `dplyr::case_match()`.
-```r
+``` r
ds_patient <-
ds_patient |>
dplyr::mutate(
@@ -233,20 +245,20 @@ ds_patient <-
knitr::include_graphics("images/codebook-race.png")
```
-Write Entire Patient-level Table
--------------------------
+## Write Entire Patient-level Table
-If the small subset works, we usually jump ahead and try all columns and rows.
+If the small subset works, we usually jump ahead and try all columns and
+rows.
-If this larger table fails, split the difference between
-(a) the smaller working example and
-(b) the larger failing example.
-See if this middle point (that has fewer rows and/or columns than the failing point)
-succeeds or fails.
-Then repeat.
-This "bisection" or "binary search" [debugging technique](https://medium.com/codecastpublication/debugging-tools-and-techniques-binary-search-2da5bb4282c7) is helpful in many areas of programming and statistical modeling.
+If this larger table fails, split the difference between (a) the smaller
+working example and (b) the larger failing example. See if this middle
+point (that has fewer rows and/or columns than the failing point)
+succeeds or fails. Then repeat. This "bisection" or "binary search"
+[debugging
+technique](https://medium.com/codecastpublication/debugging-tools-and-techniques-binary-search-2da5bb4282c7)
+is helpful in many areas of programming and statistical modeling.
-```r
+``` r
# patient-entire
ds_patient |>
REDCapR::redcap_write(
@@ -257,29 +269,33 @@ ds_patient |>
)
```
-Part 3 - Write Data: Repeating Instrument
-===================================
+# Part 3 - Write Data: Repeating Instrument
-Add Plumbing Variables
--------------------------
+## Add Plumbing Variables
-As stated in the vignette's intro,
-the structure of the dataset uploaded to the server must be precise.
-When uploading repeating instruments, there are several important columns:
+As stated in the vignette's intro, the structure of the dataset uploaded
+to the server must be precise. When uploading repeating instruments,
+there are several important columns:
-1. `record_id`: typically indicates the patient's id. (This field can be renamed for the project.)
-1. `redcap_event_name`: If the project is longitudinal or has arms, this indicates the event.
- Otherwise, you don't need to add this variable.
-1. `redcap_repeat_instrument`: Indicates the instrument/form that is repeating for these columns.
-1. `redcap_repeat_instance`: Typically a sequential positive integer (*e.g.*, 1, 2, 3, ...) indicating the order.
+1. `record_id`: typically indicates the patient's id. (This field can
+ be renamed for the project.)
+2. `redcap_event_name`: If the project is longitudinal or has arms,
+ this indicates the event. Otherwise, you don't need to add this
+ variable.
+3. `redcap_repeat_instrument`: Indicates the instrument/form that is
+ repeating for these columns.
+4. `redcap_repeat_instance`: Typically a sequential positive integer
+ (*e.g.*, 1, 2, 3, ...) indicating the order.
-The combination of these variables needs to be unique.
-Please read the [Retrieving Longitudinal and Repeating Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
+The combination of these variables needs to be unique. Please read the
+[Retrieving Longitudinal and Repeating
+Structures](https://ouhscbbmc.github.io/REDCapR/articles/longitudinal-and-repeating.html)
vignette for details of these variables and their meanings.
-You need to pass specific variables so that the REDCap server understands the hierarchical structure of the data points.
+You need to pass specific variables so that the REDCap server
+understands the hierarchical structure of the data points.
-```r
+``` r
# repeat-plumbing
ds_daily <-
ds_daily |>
@@ -305,10 +321,9 @@ REDCapR::validate_for_write(ds_daily, convert_logical_to_integer = TRUE)
ds_daily
```
-Writing Repeating Instrument Variables
--------------------------
+## Writing Repeating Instrument Variables
-```r
+``` r
# daily-entire
ds_daily |>
REDCapR::redcap_write(
@@ -319,57 +334,79 @@ ds_daily |>
)
```
-Part 4 - Next Steps
-===================================
+# Part 4 - Next Steps
+
+## More Complexity
+
+This vignette required only two data.frames, but more complex projects
+sometimes need more. For example, each repeating instrument should be
+its own data.frame and writing step. Arms and longitudinal events need
+to be considered too.
-More Complexity
--------------------------
+## Batching
-This vignette required only two data.frames, but more complex projects sometimes need more.
-For example, each repeating instrument should be its own data.frame and
-writing step. Arms and longitudinal events need to be considered too.
+By default, `REDCapR::redcap_write()` requests datasets of 100 patients
+as a time, and stacks the resulting subsets together before returning a
+data.frame. This can be adjusted to improve performance; the 'Details'
+section of `REDCapR::redcap_write()` discusses the trade offs.
-Batching
--------------------------
+I usually shoot for \~10 seconds per batch.
-By default, `REDCapR::redcap_write()` requests datasets of 100 patients as a time,
-and stacks the resulting subsets together before returning a data.frame.
-This can be adjusted to improve performance;
-the 'Details' section of `REDCapR::redcap_write()` discusses the trade offs.
+## Manual vs API
-I usually shoot for ~10 seconds per batch.
+Manual downloading/uploading might make sense if you're do the operation
+only once. But when does it ever stop after the first time?
-Manual vs API
--------------------------
+If you have trouble uploading, consider adding a few fake patients &
+measurements and then download the csv. It might reveal something you
+didn't anticipate. But be aware that it will be in the block matrix
+format (*i.e.*, everything jammed into one rectangle.)
-Manual downloading/uploading might make sense if you're do the operation only once.
-But when does it ever stop after the first time?
+## REDCap's CDIS
-If you have trouble uploading, consider adding a few fake patients & measurements
-and then download the csv.
-It might reveal something you didn't anticipate.
-But be aware that it will be in the block matrix format
-(*i.e.*, everything jammed into one rectangle.)
+The [Clinical Data Interoperability Services](https://projectredcap.org/software/cdis/) (CDIS)
+use [FHIR](https://www.hl7.org/fhir/overview.html) to move data from
+your institution's [EMR/EHR](https://www.healthit.gov/faq/what-are-differences-between-electronic-medical-records-electronic-health-records-and-personal)
+(eg, Epic, Cerner) to REDCap.
+Research staff have control over which patient records are selected or eligible.
+Conceptually it's similar to writing to REDCap's with the API,
+but at much bigger scale.
+Realistically, it takes months to get through your institution's human layers.
+Once established, a project would be populated with EMR data
+in much less development time
+--assuming the desired data models corresponds with
+FHIR [endpoints](https://hl7.org/fhir/endpoint.html).
-Notes
-===================================
+# Notes
-This vignette was originally designed for the
-[2023 R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
-_Using REDCap and R to Rapidly Produce Biomedical Publications Cleaning Medical Data_
-with [Raymond R. Balise](https://github.com/RaymondBalise), Belén Hervera, Daniel Maya, Anna Calderon, Tyler Bartholomew, Stephan Kadauke, and João Pedro Carmezim Correia.
-The entire workshop's slides are at .
+This vignette was originally designed for the [2023
+R/Medicine](https://events.linuxfoundation.org/r-medicine/) workshop,
+*Using REDCap and R to Rapidly Produce Biomedical Publications Cleaning
+Medical Data* with [Raymond R.
+Balise](https://github.com/RaymondBalise), Belén Hervera, Daniel Maya,
+Anna Calderon, Tyler Bartholomew, Stephan Kadauke, and João Pedro
+Carmezim Correia and the [2024
+R/Medicine](https://rconsortium.github.io/RMedicine_website/Program.html)
+workshop, *REDCap + R: Teaming Up in the Tidyverse*, with Stephan
+Kadauke. The workshop slides are for
+[2023](https://github.com/RaymondBalise/r_med_redcap_2023_public) and
+[2024](https://github.com/skadauke/rmedicine_2024_redcap_r_workshop).
-This work was made possible in part by the NIH grant [U54GM104938](https://taggs.hhs.gov/Detail/AwardDetail?arg_AwardNum=U54GM104938&arg_ProgOfficeCode=127)
-to the [Oklahoma Shared Clinical and Translational Resource)](http://osctr.ouhsc.edu).
+This work was made possible in part by the NIH grant
+[U54GM104938](https://taggs.hhs.gov/Detail/AwardDetail?arg_AwardNum=U54GM104938&arg_ProgOfficeCode=127)
+to the [Oklahoma Shared Clinical and Translational
+Resource)](http://osctr.ouhsc.edu).
-Session Information
-==================================================================
+# Session Information
-For the sake of documentation and reproducibility, the current report was rendered in the following environment. Click the line below to expand.
+For the sake of documentation and reproducibility, the current report
+was rendered in the following environment. Click the line below to
+expand.
- Environment
+
+Environment
+
```{r session-info, echo=FALSE}
if (requireNamespace("sessioninfo", quietly = TRUE)) {
sessioninfo::session_info()
@@ -377,4 +414,5 @@ if (requireNamespace("sessioninfo", quietly = TRUE)) {
sessionInfo()
}
```
+