---
title: Basic REDCapR Operations
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Basic REDCapR Operations}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

This vignette covers the the basic functions exposed by the [`httr`](https://github.com/r-lib/httr) and [`curl`](https://cran.r-project.org/package=curl) packages which allow you to interact with [REDCap](https://www.project-redcap.org/) through its API.

Reading REDCap Data
==================================================================

The function `redcap_read_oneshot` uses the [`httr`](https://cran.r-project.org/package=httr) package to call the REDCap API.
```{r set_options, echo=FALSE, results='hide'}
report_render_start_time <- Sys.time()

library(knitr)
library(magrittr)
requireNamespace("kableExtra")

opts_chunk$set(
  comment = NA,
  tidy    = FALSE
)

knit_print.data.frame <- function(x, ...) {
  # See https://cran.r-project.org/package=knitr/vignettes/knit_print.html
  # res = paste(c("", "", kable(x)), collapse = "\n")
  # asis_output(res)
  x %>%
    # dplyr::mutate_if(
    #   is.character,
    #   function( s ) gsub("\\n", "<br/>", s)
    # ) %>%
    kable(
      col.names = gsub("_", " ", colnames(.)),
      format    = "html"
    ) %>%
    kableExtra::kable_styling(
      bootstrap_options = c("striped", "hover", "condensed", "responsive"),
      full_width        = FALSE
    ) %>%
    c("", "", .) %>%
    paste(collapse = "\n") %>%
    asis_output()

}
# options(markdown.HTML.header = system.file("misc/vignette.css", package = "REDCapR"))
# options(width=120) #So the output is 50% wider than the default.
```

Set project-wide values
------------------------------------------------------------------

There is some information that is specific to the REDCap project, as opposed to an individual operation.  This includes the (1) uri of the server, and the (2) token for the user's project.

```{r project_values}
library(REDCapR) # Load the package into the current R session.
uri   <- "https://bbmc.ouhsc.edu/redcap/api/"
token <- "9A81268476645C4E5F03428B8AC3AA7B" # `UnitTestPhiFree` user and simple project (pid 153)
```

Read all records and fields
------------------------------------------------------------------

If no information is passed about the desired records or fields, then the entire data set is returned.  Only two parameters are required, `redcap_uri` and `token`.  Unless the `verbose` parameter is set to `FALSE`, a message will be printed on the R console with the number of records and fields returned.

```{r return_all}
# Return all records and all variables.
ds_all_rows_all_fields <- redcap_read(redcap_uri = uri, token = token)$data
ds_all_rows_all_fields # Inspect the returned dataset
```

Read a subset of the records
------------------------------------------------------------------

If only a subset of the **records** is desired, the two approaches are shown below.  The first is to pass an array (where each element is an ID) to the `records` parameter.  The second is to pass a single string (where the elements are separated by commas) to the `records_collapsed` parameter.

The first format is more natural for more R users.  The second format is what is expected by the REDCap API.  If a value for `records` is specified, but `records_collapsed` is not specified, then `redcap_read_oneshot` automatically converts the array into the format needed by the API.

```{r read_row_subset, results='hold'}
# Return only records with IDs of 1 and 3
desired_records_v1 <- c(1, 3)
ds_some_rows_v1 <- redcap_read(
  redcap_uri = uri,
  token      = token,
  records    = desired_records_v1
)$data

# Return only records with IDs of 1 and 3 (alternate way)
desired_records_v2 <- "1, 3"
ds_some_rows_v2 <- redcap_read(
  redcap_uri        = uri,
  token             = token,
  records_collapsed = desired_records_v2
)$data

ds_some_rows_v2 # Inspect the returned dataset
```

Read a subset of the fields
------------------------------------------------------------------

If only a subset of the **fields** is desired, then two approaches exist.  The first is to pass an array (where each element is an field) to the `fields` parameter.  The second is to pass a single string (where the elements are separated by commas) to the `fields_collapsed` parameter. Like with `records` and `records_collapsed` described above, this function converts the more natural format (*i.e.*, `fields`) to the format required by the API (*ie*, `fields_collapsed`) if `fields` is specified and `fields_collapsed` is not.

```{r read_field_subset}
#Return only the fields record_id, name_first, and age
desired_fields_v1 <- c("record_id", "name_first", "age")
ds_some_fields_v1 <- redcap_read(
  redcap_uri = uri,
  token      = token,
  fields     = desired_fields_v1
)$data

#Return only the fields record_id, name_first, and age (alternate way)
desired_fields_v2 <- "record_id, name_first, age"
ds_some_fields_v2 <- redcap_read(
  redcap_uri       = uri,
  token            = token,
  fields_collapsed = desired_fields_v2
)$data

ds_some_fields_v2 #Inspect the returned dataset
```

Read a subset of records, conditioned on the values in some variables
------------------------------------------------------------------

The two techniques above can be combined when your datasets are large and you don't want to pull records with certain values.  Suppose you want to select subjects from the previous dataset *if* the were born before 1960 and their weight was over 70kg.  Two calls to the server are required.  The **first call** to REDCap pulls all the records, but for only three columns: `record_id`, `dob`, and `weight`.  From this subset, identify the records that you want to pull all the data for; in this case, the desired `record_id` values are `3` & `5`.  The **second call** to REDCap pulls all the columns, but only for the identified records.

```{r read_record_field_subset}
######
## Step 1: First call to REDCap
desired_fields_v3 <- c("record_id", "dob", "weight")
ds_some_fields_v3 <- redcap_read(
  redcap_uri = uri,
  token      = token,
  fields     = desired_fields_v3
)$data

ds_some_fields_v3 #Examine the these three variables.

######
## Step 2: identify desired records, based on age & weight
before_1960 <- (ds_some_fields_v3$dob <= as.Date("1960-01-01"))
heavier_than_70_kg <- (ds_some_fields_v3$weight > 70)
desired_records_v3 <- ds_some_fields_v3[before_1960 & heavier_than_70_kg, ]$record_id

desired_records_v3 #Peek at IDs of the identified records

######
## Step 3: second call to REDCap
#Return only records that met the age & weight criteria.
ds_some_rows_v3 <- redcap_read(
  redcap_uri = uri,
  token      = token,
  records    = desired_records_v3
)$data

ds_some_rows_v3 #Examine the results.
```

Additional Returned Information
------------------------------------------------------------------

The examples above have shown only the resulting `data.frame`, by specifying `$data` at the end of the call.  However, more is available to those wanting additional information, such as:

1. The `data` object has the `data.frame`, as in the previous examples.
1. The `success` boolean value indicates if `redcap_read_oneshot` believes the operation completed as intended.
1. The `status_codes` is a collection of [http status codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes), separated by semicolons.  There is one code for each batch attempted.
1. The `outcome_messages`: A collection of human readable strings indicating the operations' semicolons.  There is one code for each batch attempted.  In an unsuccessful operation, it should contain diagnostic information.
1. The `records_collapsed` field passed to the API.  This shows which record subsets, if any, were requested.
1. The `fields_collapsed` fields passed to the API.  This shows which field subsets, if any, were requested.
1. The `elapsed_seconds` measures the duration of the call.

```{r read_not_just_dataframe}
#Return only the fields record_id, name_first, and age
all_information <- redcap_read(
  redcap_uri = uri,
  token      = token,
  fields     = desired_fields_v1
)
all_information #Inspect the additional 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.

<details>
  <summary>Environment <span class="glyphicon glyphicon-plus-sign"></span></summary>
```{r session-info, echo=FALSE}
if (requireNamespace("sessioninfo", quietly = TRUE)) {
  sessioninfo::session_info()
} else {
  sessionInfo()
}
```
</details>

```{r session-duration, echo=FALSE}
report_render_duration_in_seconds <- round(as.numeric(difftime(Sys.time(), report_render_start_time, units = "secs")))
```

Report rendered by `r Sys.info()["user"]` at `r strftime(Sys.time(), "%Y-%m-%d, %H:%M %z")` in `r report_render_duration_in_seconds` seconds.
