OuhscBbmc / REDCapR

R utilities for interacting with REDCap
https://ouhscbbmc.github.io/REDCapR
Other
112 stars 46 forks source link

Adding repeat support to redcap_delete #517

Open pbchase opened 7 months ago

pbchase commented 7 months ago

I am considering writing support for repeating instruments and events to redcap_delete to meet a local need. REDCap supports the deletion of repeats via the API, but VUMC's implementation raises some questions.

Looking at REDCap 14.1.3, API/record/delete.php, the function delRecords() will iterate over an array of record IDs, but it treats event, instrument, and repeat_instance as scalars. So a deletion of a vector of n repeats of an instrument for one record would require n API calls. You could delete a vector of record IDs for a single repeat of a single instrument, but you would have to build that data structure from the dataset of records and repeats you plan to delete.

It would be nice to ignore the capabilities of the Delete Records API method. I'd prefer to name the repeats I want to delete by record_id, instrument name(s), and repeating instance numbers and let the delete function manage that complexity. This code generates a dataframe of repeats of a form to delete, then transforms them into a data structure that aligns with the capabilities of the REDCap Delete Records API method

library(tidyverse)
library(purrr)

size <- 8
floor <- c(5,6,7,8,9)
repeats_to_delete <- tibble(
  record_id = sample(c(1,2,3,4,5), size = size, replace = T),
  redcap_repeat_instrument = sample(c("plasma_from_edta"), size = size, replace = T),
  redcap_repeat_instance = purrr::map(seq(1, size), ~ sample(floor, size = 1):9)
) |>
  unnest(redcap_repeat_instance) |>
  distinct()

repeats_to_delete

repeats_to_delete |>
  group_by(redcap_repeat_instrument, redcap_repeat_instance) |>
  nest(data = "record_id")

e.g.

> library(tidyverse)
> library(purrr)
> size <- 8
> floor <- c(5,6,7,8,9)
> repeats_to_delete <- tibble(
+   record_id = sample(c(1,2,3,4,5), size = size, replace = T),
+   redcap_repeat_instrument = sample(c("plasma_from_edta"), size = size, replace = T),
+   redcap_repeat_instance = purrr::map(seq(1, size), ~ sample(floor, size = 1):9)
+ ) |>
+   unnest(redcap_repeat_instance) |>
+   distinct()
> repeats_to_delete
# A tibble: 13 × 3
   record_id redcap_repeat_instrument redcap_repeat_instance
       <dbl> <chr>                                     <int>
 1         1 plasma_from_edta                              7
 2         1 plasma_from_edta                              8
 3         1 plasma_from_edta                              9
 4         3 plasma_from_edta                              8
 5         3 plasma_from_edta                              9
 6         4 plasma_from_edta                              9
 7         5 plasma_from_edta                              7
 8         5 plasma_from_edta                              8
 9         5 plasma_from_edta                              9
10         4 plasma_from_edta                              8
11         2 plasma_from_edta                              7
12         2 plasma_from_edta                              8
13         2 plasma_from_edta                              9
> repeats_to_delete |>
+   group_by(redcap_repeat_instrument, redcap_repeat_instance) |>
+   nest(data = "record_id") |>
+   ungroup()
# A tibble: 3 × 3
  redcap_repeat_instrument redcap_repeat_instance data            
  <chr>                                     <int> <list>          
1 plasma_from_edta                              7 <tibble [3 × 1]>
2 plasma_from_edta                              8 <tibble [5 × 1]>
3 plasma_from_edta                              9 <tibble [5 × 1]>

This method works well with multiple instruments as well. I just simplified the above data for readability. It does not address repeating events, but it could with modifications.

With such a data frame, one could call the function like this:

redcap_delete(
  redcap_uri = my_uri,
  token = my_token,
  records_to_delete = repeats_to_delete$record_id,
  instruments_to_delete = repeats_to_delete$redcap_repeat_instrument,
  repeats_to_delete = repeats_to_delete$redcap_repeat_instance,
  ...
)

I'm curious to get feedback on which data transformations to put into redcap_delete's scope and how best to pass the data.