From c5385ad7fdfc062f5c0b3e07f197484731fa64f3 Mon Sep 17 00:00:00 2001 From: Shawn Garbett Date: Tue, 25 Aug 2026 10:29:56 -0500 Subject: [PATCH 1/2] Roxygen version bump --- DESCRIPTION | 2 +- NAMESPACE | 58 +++--- R/fieldValidationAndCasting.R | 327 ++++++++++++++++--------------- R/redcapAPI-package.R | 23 ++- man/fieldValidationAndCasting.Rd | 15 +- man/fileRepositoryMethods.Rd | 3 +- man/recordsMethods.Rd | 2 +- man/redcapConnection.Rd | 4 +- man/redcapDataStructures.Rd | 13 -- 9 files changed, 215 insertions(+), 232 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ab62ba10..dd33d47d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -55,6 +55,6 @@ URL: https://github.com/vubiostat/redcapAPI BugReports: https://github.com/vubiostat/redcapAPI/issues Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.3 VignetteBuilder: R.rsp Config/build/clean-inst-doc: false +Config/roxygen2/version: 8.1.0 diff --git a/NAMESPACE b/NAMESPACE index 418602b2..03b4bc75 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -222,33 +222,39 @@ export(vectorToApiBodyList) export(widerRepeated) import(checkmate) importFrom(chron,times) -importFrom(curl,curl_fetch_memory) -importFrom(curl,curl_version) -importFrom(curl,form_file) -importFrom(curl,handle_cookies) -importFrom(curl,handle_reset) -importFrom(curl,handle_setform) -importFrom(curl,handle_setheaders) -importFrom(curl,handle_setopt) -importFrom(curl,new_handle) -importFrom(curl,parse_headers_list) -importFrom(jsonlite,fromJSON) -importFrom(jsonlite,toJSON) -importFrom(labelVector,get_label) -importFrom(labelVector,is.labelled) -importFrom(labelVector,set_label) +importFrom(curl, + curl_fetch_memory, + curl_version, + form_file, + handle_cookies, + handle_reset, + handle_setheaders, + handle_setopt, + new_handle +) +importFrom(jsonlite, + fromJSON, + toJSON +) +importFrom(labelVector, + get_label, + is.labelled, + set_label +) importFrom(lubridate,parse_date_time) importFrom(mime,guess_type) importFrom(shelter,unlockKeys) importFrom(stats,reshape) -importFrom(utils,browseURL) -importFrom(utils,capture.output) -importFrom(utils,compareVersion) -importFrom(utils,head) -importFrom(utils,modifyList) -importFrom(utils,osVersion) -importFrom(utils,packageVersion) -importFrom(utils,read.csv) -importFrom(utils,tail) -importFrom(utils,write.csv) -importFrom(utils,write.table) +importFrom(utils, + browseURL, + capture.output, + compareVersion, + head, + modifyList, + osVersion, + packageVersion, + read.csv, + tail, + write.csv, + write.table +) diff --git a/R/fieldValidationAndCasting.R b/R/fieldValidationAndCasting.R index a89a43ad..708060b6 100644 --- a/R/fieldValidationAndCasting.R +++ b/R/fieldValidationAndCasting.R @@ -1,23 +1,23 @@ #' @name fieldValidationAndCasting #' @title Helper functions for `exportRecordsTyped` Validation and Casting -#' @description This set of functions assists in validating that the content of -#' fields coming from REDCap match the MetaData, allowing for a +#' @description This set of functions assists in validating that the content of +#' fields coming from REDCap match the MetaData, allowing for a #' validation report to provided. The cast helpers allow for transforming -#' the REDCap data into R data types and allowing the user to customize +#' the REDCap data into R data types and allowing the user to customize #' the end product. -#' +#' #' @param x `character`. A vector to check. #' @param rx `character`. The regular expression pattern to check. #' @param coding named `character` vector. The defined coding from the meta data. #' @param field_name `character(1)`. Name of the field(s) -#' @param dec_symbol `character(1)`. The symbol in the field used to -#' denote a decimal. +#' @param dec_symbol `character(1)`. The symbol in the field used to +#' denote a decimal. #' @param n_dec `integerish(1)`. The number of decimal places permitted -#' by the field validation. -#' @param checked `character`. Values to recognize as checked in a +#' by the field validation. +#' @param checked `character`. Values to recognize as checked in a #' checkbox field. -#' @param FUN `function`. A function that takes a character vector. -#' @param ... Consumes anything else passed to function. I.e., field_name and +#' @param FUN `function`. A function that takes a character vector. +#' @param ... Consumes anything else passed to function. I.e., field_name and #' coding. #' #' @details Functions passed to the `na`, `validation`, and @@ -26,134 +26,134 @@ #' functions are expected to return a logical vector of the same length as the #' column processed. Helper routines #' are provided here for common cases to construct these functions. -#' +#' #' ## Missing Data Detection -#' +#' #' `na_values` is a helper function to create a list of functions #' to test for NA based on field type. Useful for bulk override of #' NA detection for a project. The output can be directly passed to the `na` #' parameter of [exportRecordsTyped()]. -#' +#' #' Missing data detection is performed ahead of validation. Data that are found -#' to be missing are excluded from validation reports. -#' +#' to be missing are excluded from validation reports. +#' #' REDCap users may define project-level missing value codes. If such codes #' are defined, they can be seen in Project Setup > Additional Customizations > -#' Missing Data Codes. They will also be displayed in the project's Codebook. +#' Missing Data Codes. They will also be displayed in the project's Codebook. #' Project-level missing data codes cannot be accessed via the API, meaning -#' `redcapAPI` is unable to assist in determining if a project has any. The -#' most likely symptom of project-level codes is a high frequency of +#' `redcapAPI` is unable to assist in determining if a project has any. The +#' most likely symptom of project-level codes is a high frequency of #' values failing validation (See `vignette("redcapAPI-missing-data-detection")`). -#' +#' #' ## Validation Functions -#' +#' #' `isNAorBlank` returns TRUE/FALSE if field is NA or blank. Helper #' function for constructing `na` overrides in [exportRecordsTyped()]. -#' -#' `valRx` constructs a validation function from a regular expression pattern. +#' +#' `valRx` constructs a validation function from a regular expression pattern. #' The function returns a TRUE/FALSE if the value matches the pattern. -#' -#' `valChoice` constructs a validation function from a set of choices +#' +#' `valChoice` constructs a validation function from a set of choices #' defined in the MetaData. The functions returns a TRUE/FALSE if the value #' matches one of the choices. -#' -#' `valPhone` constructs a validation function for (North American) +#' +#' `valPhone` constructs a validation function for (North American) #' phone numbers. It removes punctuation and spaces prior to validating #' with the regular expression. -#' -#' `valSkip` is a function that supports skipping the validation for +#' +#' `valSkip` is a function that supports skipping the validation for #' a field type. It returns a `TRUE` value for each record, regardless #' of its value. Validation skipping has occasional utility when importing #' certain field types (such as `bioportal` or `sql`) where not all of the #' eventual choices are available in the project yet. -#' +#' #' `skip_validation` is a list of functions that just returns TRUE for #' all data passed in. -#' +#' #' ## Casting Functions -#' -#' `castLabel` constructs a casting function for multiple choice variables. -#' The field will be cast to return the choice label (generally more human readable). -#' `castLabelCharacter` is an equivalent casting function that returns +#' +#' `castLabel` constructs a casting function for multiple choice variables. +#' The field will be cast to return the choice label (generally more human readable). +#' `castLabelCharacter` is an equivalent casting function that returns #' a `character` vector instead of a `factor`. -#' +#' #' `castCode` constructs a casting function for multiple choice variables. #' Similar to `castLabel`, but the choice value is returned instead. The #' values are typically more compact and their meaning may not be obvious. #' `castCodeCharacter` is an equivalent casting function that retuns #' a `character` vector instead of a `factor`. -#' +#' #' `castRaw` constructs a casting function that returns the content -#' from REDCap as it was received. It is functionally equivalent to `identity`. -#' For multiple choice variables, the result will be coerced to numeric, if possible; -#' otherwise, the result is character vector. -#' +#' from REDCap as it was received. It is functionally equivalent to `identity`. +#' For multiple choice variables, the result will be coerced to numeric, if possible; +#' otherwise, the result is character vector. +#' #' `castChecked` constructs a casting function for checkbox fields. It #' returns values in the form of Unchecked/Checked. `castCheckedCharacter` -#' is an equivalent casting function that returns a `character` vector +#' is an equivalent casting function that returns a `character` vector #' instead of a `factor`. -#' +#' #' `castCheckLabel` and `castCheckCode` also construct casting functions -#' for checkbox fields. For both, unchecked variables are cast to an empty -#' string (""). Checked variables are cast to the option label and option code, +#' for checkbox fields. For both, unchecked variables are cast to an empty +#' string (""). Checked variables are cast to the option label and option code, #' respectively. `castCheckLabelCharacter` and `castCheckCodeCharacter` -#' are equivalent casting functions that returns a `character` vector +#' are equivalent casting functions that returns a `character` vector #' instead of a `factor`. -#' +#' #' `castCheckForImport` is a special case function to allow the user to #' specify exactly which values are to be considered "Checked". Values that #' match are returned as `1` and all other values are returned as `0`. This is -#' motivated by the special case where the coding on a checkbox includes +#' motivated by the special case where the coding on a checkbox includes #' "0, Option". In the resulting field `checkbox___0`, a coded value -#' of `0` actually implies the choice was selected. In order to perform an -#' import on such data, it is necessary to cast it using +#' of `0` actually implies the choice was selected. In order to perform an +#' import on such data, it is necessary to cast it using #' `castCheckForImport(c("0"))`. -#' -#' `castDpNumeric` is a casting function for fields that use the -#' `number_ndp_comma` field type (where `n` is the number of +#' +#' `castDpNumeric` is a casting function for fields that use the +#' `number_ndp_comma` field type (where `n` is the number of #' decimal places). This function will convert the values to numeric -#' values for use in analysis. This is a function that returns the -#' appropriate casting function, thus the appropriate usage when using +#' values for use in analysis. This is a function that returns the +#' appropriate casting function, thus the appropriate usage when using #' the defaults is `cast = list(number_1dp_comma = castDpNumeric())` #' (using the parentheses). -#' +#' #' `castDpCharacter` is a casting function to return fields that use -#' `number_ndp_comma` field types to character strings for import. This -#' is a function that returns the appropriate casting function, thus the -#' appropriate usage when casting for one decimal place is +#' `number_ndp_comma` field types to character strings for import. This +#' is a function that returns the appropriate casting function, thus the +#' appropriate usage when casting for one decimal place is #' `cast = list(number_1dp_comma = castDpCharacter(1))`. -#' -#' `castTimeHHMM` and `castTimeMMSS` are casting functions to -#' facilitate importing data. They convert time data into a character format -#' that will pass the API requirements. -#' -#' `castLogical` is a casting function that returns a logical vector for -#' common, binary-type responses. It is well suited to changing true/false, +#' +#' `castTimeHHMM` and `castTimeMMSS` are casting functions to +#' facilitate importing data. They convert time data into a character format +#' that will pass the API requirements. +#' +#' `castLogical` is a casting function that returns a logical vector for +#' common, binary-type responses. It is well suited to changing true/false, #' yes/no, and checkbox fields into logical vectors, as it returns `TRUE` if #' the value is one of `c("1", "true", "yes")` and returns `FALSE` otherwise. -#' -#' +#' +#' #' ## Casting Lists #' `raw_cast` overrides all casting if passed as the `cast` #' parameter. It is important the the validation specified matches #' the chosen cast. For fully raw it should be `skip_validation`. -#' +#' #' `default_cast_no_factor` is a list of casting functions that matches #' all of the default casts but with the exception that any fields that would -#' have been cast to factors will instead be cast to characters. It is +#' have been cast to factors will instead be cast to characters. It is #' provided for the user that prefers to work absent factors. The list #' `default_cast_character` is equivalent and is provided for those that #' prefer to describe their casting in terms of what the result is (and not #' what it is not). -#' -#' -#' @return -#' -#' Validation and casting functions return the objects indicated in the +#' +#' +#' @return +#' +#' Validation and casting functions return the objects indicated in the #' following table: -#' +#' #' | Function Name | Object Type Returned | -#' |---------------------------|----------------------| +#' |---------------------------|----------------------| #' | `isNAOrBlank` | `logical` | #' | `valRx` | `logical` | #' | `valChoice` | `logical` | @@ -169,32 +169,32 @@ #' | `castCheckLabel` | `factor` | #' | `castCheckLabelCharacter` | `character` | #' | `castCheckCode` | `factor` | -#' | `castCheckCodeCharacter` | `character` | +#' | `castCheckCodeCharacter` | `character` | #' | `castCheckForImport` | `numeric` | #' | `castDpNumeric` | `numeric` | #' | `castDpCharacter` | `character` | #' | `castTimeHHMM` | `character` | #' | `castTimeMMSS` | `character` | #' | `castLogical` | `logical` | -#' -#' -#' @seealso +#' +#' +#' @seealso #' [fieldCastingFunctions()], \cr #' [exportRecordsTyped()], \cr #' [exportReportsTyped()] -#' +#' #' ## Vignettes -#' +#' #' `vignette("redcapAPI-casting-data")`\cr #' `vignette("redcapAPI-missing-data-detection")`\cr #' `vignette("redcapAPI-data-validation)`\cr #' `vignette("redcapAPI-faq)` -#' +#' #' @examples #' \dontrun{ #' # Make a custom function to give special treatment to a field. -#' # In this function, the field "field_name_to_skip" will -#' # be cast using `castRaw`. All other fields will be cast +#' # In this function, the field "field_name_to_skip" will +#' # be cast using `castRaw`. All other fields will be cast #' # using `castCode` #' customCastCode <- function(x, field_name, coding){ #' if (field_name == "field_name_to_skip"){ @@ -204,7 +204,7 @@ #' } #' } #' } -#' +#' ##################################################################### # Validation #### @@ -245,7 +245,7 @@ na_values <- function(FUN) null.ok = FALSE, add = coll) checkmate::reportAssertions(coll) - + l <- lapply(FIELD_TYPES, function(f) FUN) names(l) <- FIELD_TYPES l @@ -258,7 +258,7 @@ na_values <- function(FUN) #' @export castLabel <- function(x, field_name, coding){ code_match <- getCodingIndex(x, coding) - + factor(unname(coding[code_match]), levels = coding, labels = names(coding)) } @@ -272,7 +272,7 @@ castLabelCharacter <- function(x, field_name, coding){ #' @export castCode <- function(x, field_name, coding){ code_match <- getCodingIndex(x, coding) - + factor(unname(coding[code_match]), levels = coding, labels = coding) } @@ -286,11 +286,11 @@ castCodeCharacter <- function(x, field_name, coding){ #' @export castRaw <- function(x, field_name, coding){ warnOfZeroCodedCheckCasting(field_name, x) - - raw <- + + raw <- if (grepl(".*___(.*)", field_name)){ - ifelse(!is.na(x), - as.character((x %in% getCheckedValue(coding, field_name)) + 0L), + ifelse(!is.na(x), + as.character((x %in% getCheckedValue(coding, field_name)) + 0L), NA) } else { code_match <- getCodingIndex(x, coding) @@ -303,11 +303,11 @@ castRaw <- function(x, field_name, coding){ #' @export castChecked <- function(x, field_name, coding){ checked_value <- getCheckedValue(coding, field_name) - + warnOfZeroCodedCheckCasting(field_name, x) - - x_checked <- x %in% checked_value - + + x_checked <- x %in% checked_value + factor(c("Unchecked", "Checked")[(x_checked)+1], levels=c("Unchecked", "Checked")) } @@ -321,21 +321,21 @@ castCheckedCharacter <- function(x, field_name, coding){ #' @export castCheckLabel <- function(x, field_name, coding){ checked_value <- getCheckedValue(coding, field_name) - + warnOfZeroCodedCheckCasting(field_name, x) - - x_checked <- x %in% checked_value - + + x_checked <- x %in% checked_value + # Sets the level and label while accomodating 0 coded check values # (0 is not considered a `checked_value` in this case, so must be handled by force) - + is_zero_coded <- isZeroCodedCheckField(field_name) - + the_level <- if (is_zero_coded) "0" else checked_value[1] the_label <- if (is_zero_coded) checked_value[1] else names(checked_value)[1] - - factor(unname(c("", the_level)[(x_checked) + 1]), - levels=c("", the_level), + + factor(unname(c("", the_level)[(x_checked) + 1]), + levels=c("", the_level), labels=c("", the_label)) } @@ -349,21 +349,21 @@ castCheckLabelCharacter <- function(x, field_name, coding){ #' @export castCheckCode <- function(x, field_name, coding){ checked_value <- getCheckedValue(coding, field_name) - + warnOfZeroCodedCheckCasting(field_name, x) - - x_checked <- x %in% checked_value - + + x_checked <- x %in% checked_value + # Sets the level and label while accomodating 0 coded check values # (0 is not considered a `checked_value` in this case, so must be handled by force) - + is_zero_coded <- isZeroCodedCheckField(field_name) - + the_level <- if (is_zero_coded) "0" else checked_value[1] the_label <- if (is_zero_coded) "0" else checked_value[1] - - factor(unname(c("", the_level)[(x_checked) + 1]), - levels=c("", the_level), + + factor(unname(c("", the_level)[(x_checked) + 1]), + levels=c("", the_level), labels=c("", the_label)) } @@ -379,21 +379,21 @@ castCheckCodeCharacter <- function(x, field_name, coding){ castCheckForImport <- function(checked = c("Checked", "1")){ function(x, coding, field_name){ is_na <- is.na(x) - + out <- (x %in% checked) + 0L out[is_na] <- NA out } } -# utility function returns the index of the codebook matching the +# utility function returns the index of the codebook matching the # content of the vector. Permits accurate matching without foreknowledge # of whether the data are coded or labeled. getCodingIndex <- function(x, coding){ code_match <- match(as.character(x), coding) - - ifelse(is.na(match(x, coding)), - match(x, names(coding)), + + ifelse(is.na(match(x, coding)), + match(x, names(coding)), code_match) } @@ -401,31 +401,31 @@ getCodingIndex <- function(x, coding){ getCheckedValue <- function(coding, field_name){ this_code <- sub(REGEX_CHECKBOX_FIELD_NAME, # defined in constants.R "\\2", field_name, perl = TRUE) - # to match, we will convert all of the punctuation to underscore. + # to match, we will convert all of the punctuation to underscore. # this is consistent with how REDCap converts codes to variable names. - # for instance, a checkbox with code 4-3, label produce variable name checkbox___4_3 - this_code_index <- match(this_code, - tolower(gsub("[[:punct:]]", "_", coding))) - - checked_value <- c(coding[this_code_index], - names(coding)[this_code_index], - "1", - "Checked", + # for instance, a checkbox with code 4-3, label produce variable name checkbox___4_3 + this_code_index <- match(this_code, + tolower(gsub("[[:punct:]]", "_", coding))) + + checked_value <- c(coding[this_code_index], + names(coding)[this_code_index], + "1", + "Checked", "yes") - - # When casting from raw, we do not want to consider "0" as checked + + # When casting from raw, we do not want to consider "0" as checked # for zero coded fields. if (isZeroCodedCheckField(field_name)){ checked_value <- checked_value[-1] - } - + } + checked_value } # Coerce to a numeric vector if possible, otherwise return the original value coerceNumericIfAble <- function(x){ - x <- tryCatch(as.numeric(x), - warning = function(cond) x, + x <- tryCatch(as.numeric(x), + warning = function(cond) x, error = function(cond) x) x } @@ -447,8 +447,8 @@ castDpNumeric <- function(dec_symbol = ","){ castDpCharacter <- function(n_dec, dec_symbol = ","){ function(x, field_name, coding){ - x[!is.na(x)] <- format(round(as.numeric(x[!is.na(x)]), n_dec), - nsmall = n_dec, + x[!is.na(x)] <- format(round(as.numeric(x[!is.na(x)]), n_dec), + nsmall = n_dec, decimal.mark = dec_symbol) x } @@ -469,6 +469,7 @@ castTimeHHMM <- function(x, field_name, coding){ #' @rdname fieldValidationAndCasting #' @export +#' @importFrom utils tail castTimeMMSS <- function(x, field_name, coding){ x <- as.character(x) @@ -515,8 +516,8 @@ raw_cast <- list( select = NA, radio = NA, dropdown = NA, - sql = NA, - system = NA, + sql = NA, + system = NA, bioportal = NA ) @@ -529,13 +530,13 @@ default_cast_no_factor <- list( datetime_seconds_ = function(x, ...) as.POSIXct(x, format = "%Y-%m-%d %H:%M:%S"), time_mm_ss = function(x, ...) chron::times(ifelse(is.na(x),NA,paste0("00:",x)), format=c(times="h:m:s")), time_hh_mm_ss = function(x, ...) chron::times(x, format=c(times="h:m:s")), - time = function(x, ...) chron::times(gsub("(^\\d{2}:\\d{2}$)", "\\1:00", x), + time = function(x, ...) chron::times(gsub("(^\\d{2}:\\d{2}$)", "\\1:00", x), format=c(times="h:m:s")), float = as.numeric, number = as.numeric, - number_1dp = as.numeric, + number_1dp = as.numeric, number_1dp_comma_decimal = castDpNumeric(), - number_2dp = as.numeric, + number_2dp = as.numeric, number_2dp_comma_decimal = castDpNumeric(), calc = as.numeric, int = as.integer, @@ -547,8 +548,8 @@ default_cast_no_factor <- list( select = castLabelCharacter, radio = castLabelCharacter, dropdown = castLabelCharacter, - sql = castLabelCharacter, - system = castLabelCharacter, + sql = castLabelCharacter, + system = castLabelCharacter, bioportal = castLabelCharacter ) @@ -563,8 +564,8 @@ default_cast_character <- default_cast_no_factor .default_validate <- list( # REGEX values defined in constants.R - date_ = function(x, ...) !is.na(as.POSIXct(x, format = "%Y-%m-%d")), - datetime_ = function(x, ...) !is.na(as.POSIXct(x, format = "%Y-%m-%d %H:%M")), + date_ = function(x, ...) !is.na(as.POSIXct(x, format = "%Y-%m-%d")), + datetime_ = function(x, ...) !is.na(as.POSIXct(x, format = "%Y-%m-%d %H:%M")), datetime_seconds_ = function(x, ...) !is.na(as.POSIXct(x, format = "%Y-%m-%d %H:%M:%S")), time_mm_ss = valRx(REGEX_TIME_MMSS), time_hh_mm_ss = valRx(REGEX_TIME_HHMMSS), @@ -581,7 +582,7 @@ default_cast_character <- default_cast_no_factor select = valChoice, radio = valChoice, dropdown = valChoice, - sql = valChoice, + sql = valChoice, bioportal = valSkip ) @@ -591,13 +592,13 @@ default_cast_character <- default_cast_no_factor datetime_seconds_ = function(x, ...) as.POSIXct(x, format = "%Y-%m-%d %H:%M:%S"), time_mm_ss = function(x, ...) chron::times(ifelse(is.na(x),NA,paste0("00:",x)), format=c(times="h:m:s")), time_hh_mm_ss = function(x, ...) chron::times(x, format=c(times="h:m:s")), - time = function(x, ...) chron::times(gsub("(^\\d{2}:\\d{2}$)", "\\1:00", x), + time = function(x, ...) chron::times(gsub("(^\\d{2}:\\d{2}$)", "\\1:00", x), format=c(times="h:m:s")), float = as.numeric, number = as.numeric, - number_1dp = as.numeric, + number_1dp = as.numeric, number_1dp_comma_decimal = castDpNumeric(), - number_2dp = as.numeric, + number_2dp = as.numeric, number_2dp_comma_decimal = castDpNumeric(), calc = as.numeric, int = as.integer, @@ -609,8 +610,8 @@ default_cast_character <- default_cast_no_factor select = castLabel, radio = castLabel, dropdown = castLabel, - sql = castLabel, - system = castLabel, + sql = castLabel, + system = castLabel, bioportal = castLabel ) @@ -624,10 +625,10 @@ default_cast_character <- default_cast_no_factor time_mm_ss = function(x, ...) valRx(REGEX_HHMMSS)(x) | valRx(REGEX_TIME_MMSS)(x), time_hh_mm_ss = function(x, ...) valRx(REGEX_HHMMSS)(x) | valRx(REGEX_TIME_HHMMSS)(x), time = function(x, ...) valRx(REGEX_HHMMSS)(x) | valRx(REGEX_TIME)(x), - alpha_only = valRx(REGEX_LETTERS_ONLY), + alpha_only = valRx(REGEX_LETTERS_ONLY), float = valRx(REGEX_FLOAT), - number = valRx(REGEX_NUMBER), - number_1dp = valRx(REGEX_NUMBER), + number = valRx(REGEX_NUMBER), + number_1dp = valRx(REGEX_NUMBER), number_1dp_comma_decimal = valRx(REGEX_NUMBER), number_2dp = valRx(REGEX_NUMBER), number_2dp_comma_decimal = valRx(REGEX_NUMBER), @@ -659,9 +660,9 @@ default_cast_character <- default_cast_no_factor alpha_only = as.character, float = as.character, number = as.character, - number_1dp = castDpCharacter(1, dec_symbol = "."), + number_1dp = castDpCharacter(1, dec_symbol = "."), number_1dp_comma_decimal = castDpCharacter(1), - number_2dp = castDpCharacter(2, dec_symbol = "."), + number_2dp = castDpCharacter(2, dec_symbol = "."), number_2dp_comma_decimal = castDpCharacter(2), calc = as.character, int = function(x, ...) as.character(as.integer(x)), @@ -673,12 +674,12 @@ default_cast_character <- default_cast_no_factor select = castRaw, radio = castRaw, dropdown = castRaw, - email = as.character, + email = as.character, phone = as.character, - zipcode = as.character, + zipcode = as.character, slider = as.numeric, - sql = castRaw, - system = castRaw, + sql = castRaw, + system = castRaw, bioportal = castCodeCharacter ) @@ -690,14 +691,14 @@ FIELD_TYPES <- c( "time_hh_mm_ss", "time", "float", "number", "calc", "int", "integer", "yesno", "truefalse", "checkbox", "form_complete", "select", - "radio", "dropdown", "sql", "system", + "radio", "dropdown", "sql", "system", "bioportal") ##################################################################### # -# Validation lists +# Validation lists #' @rdname fieldValidationAndCasting #' @export diff --git a/R/redcapAPI-package.R b/R/redcapAPI-package.R index 34569d7b..2a4daf3e 100644 --- a/R/redcapAPI-package.R +++ b/R/redcapAPI-package.R @@ -1,19 +1,19 @@ #' Access data, meta data, and files from REDCap using the API #' -#' REDCap is a database development tool built on MySQL. Visit +#' REDCap is a database development tool built on MySQL. Visit #' \url{https://projectredcap.org} for more information. REDCap #' provides an API through which data, the data dictionary, files, and -#' project information can be accessed. The `redcapAPI` package -#' facilitates the use of these functions and -#' simplifies the work needed to prepare data for +#' project information can be accessed. The `redcapAPI` package +#' facilitates the use of these functions and +#' simplifies the work needed to prepare data for #' analysis. -#' -#' The user should also refer to their institution's REDCap API -#' documentation as a primary resource of what is available. Different -#' versions of REDCap support different features--the user's REDCap -#' API documentation will address the features specific to their version +#' +#' The user should also refer to their institution's REDCap API +#' documentation as a primary resource of what is available. Different +#' versions of REDCap support different features--the user's REDCap +#' API documentation will address the features specific to their version #' of REDCap. -#' +#' #' @name redcapAPI #' @keywords internal #' @import checkmate @@ -25,7 +25,6 @@ #' @importFrom lubridate parse_date_time #' @importFrom mime guess_type #' @importFrom stats reshape -#' @importFrom utils capture.output compareVersion head modifyList -#' osVersion packageVersion read.csv tail write.csv write.table +#' @importFrom utils capture.output compareVersion head modifyList osVersion packageVersion read.csv tail write.csv write.table "_PACKAGE" diff --git a/man/fieldValidationAndCasting.Rd b/man/fieldValidationAndCasting.Rd index 07da41ff..edfbbe3b 100644 --- a/man/fieldValidationAndCasting.Rd +++ b/man/fieldValidationAndCasting.Rd @@ -1,6 +1,5 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/fieldValidationAndCasting.R -\docType{data} \name{fieldValidationAndCasting} \alias{fieldValidationAndCasting} \alias{isNAorBlank} @@ -31,15 +30,6 @@ \alias{default_cast_character} \alias{skip_validation} \title{Helper functions for \code{exportRecordsTyped} Validation and Casting} -\format{ -An object of class \code{list} of length 21. - -An object of class \code{list} of length 25. - -An object of class \code{list} of length 25. - -An object of class \code{list} of length 21. -} \usage{ isNAorBlank(x, ...) @@ -286,8 +276,8 @@ what it is not). \examples{ \dontrun{ # Make a custom function to give special treatment to a field. -# In this function, the field "field_name_to_skip" will -# be cast using `castRaw`. All other fields will be cast +# In this function, the field "field_name_to_skip" will +# be cast using `castRaw`. All other fields will be cast # using `castCode` customCastCode <- function(x, field_name, coding){ if (field_name == "field_name_to_skip"){ @@ -311,4 +301,3 @@ customCastCode <- function(x, field_name, coding){ \verb{vignette("redcapAPI-faq)} } } -\keyword{datasets} diff --git a/man/fileRepositoryMethods.Rd b/man/fileRepositoryMethods.Rd index effc9ebb..fa762359 100644 --- a/man/fileRepositoryMethods.Rd +++ b/man/fileRepositoryMethods.Rd @@ -1,6 +1,7 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/docsFileRepositoryMethods.R, -% R/exportFileRepository.R, R/importFileRepository.R, R/deleteFileRepository.R +% R/exportFileRepository.R, R/importFileRepository.R, +% R/deleteFileRepository.R \name{fileRepositoryMethods} \alias{fileRepositoryMethods} \alias{exportFileRepository} diff --git a/man/recordsMethods.Rd b/man/recordsMethods.Rd index 8b9ddee0..431f792f 100644 --- a/man/recordsMethods.Rd +++ b/man/recordsMethods.Rd @@ -138,7 +138,7 @@ be retrieved. When \code{FALSE}, these fields must be explicitly requested.} \item{colClasses}{Named \code{character} vector. Column classes passed to -\code{\link[utils:read.table]{utils::read.csv()}} calls. +\code{\link[utils:read.csv]{utils::read.csv()}} calls. Useful to force the interpretation of a column in a specific type and avoid an unexpected recast.} diff --git a/man/redcapConnection.Rd b/man/redcapConnection.Rd index 02fcd392..7339ccce 100644 --- a/man/redcapConnection.Rd +++ b/man/redcapConnection.Rd @@ -48,7 +48,7 @@ offlineConnection( \item{token}{\code{character(1)} REDCap API token} -\item{config}{A list to be passed to \link[curl:handle]{curl::handle_setopt}. This allows the +\item{config}{A list to be passed to \link[curl:handle_setopt]{curl::handle_setopt}. This allows the user to set additional configurations for the API calls, such as certificates, SSL version, etc. For the majority of users, this does not need to be altered.} @@ -197,7 +197,7 @@ Tokens are specific to a project, and a token must be created for each project for which the user wishes to use the API. Additional Curl option can be set in the \code{config} argument. See the documentation -for \link[curl:handle]{curl::handle_setopt} for more curl options. +for \link[curl:handle_setopt]{curl::handle_setopt} for more curl options. } \subsection{Specific to Offline Connections}{ diff --git a/man/redcapDataStructures.Rd b/man/redcapDataStructures.Rd index eb6f19ae..c6ac2c80 100644 --- a/man/redcapDataStructures.Rd +++ b/man/redcapDataStructures.Rd @@ -1,6 +1,5 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/redcapDataStructure.R -\docType{data} \name{redcapDataStructures} \alias{redcapDataStructures} \alias{validateRedcapData} @@ -10,17 +9,6 @@ \alias{REDCAP_METADATA_VALIDATION_TYPE} \alias{REDCAP_REPEAT_INSTRUMENT_STRUCTURE} \title{REDCap Data Structures} -\format{ -An object of class \code{character} of length 5. - -An object of class \code{character} of length 5. - -An object of class \code{character} of length 11. - -An object of class \code{character} of length 25. - -An object of class \code{data.frame} with 0 rows and 3 columns. -} \usage{ validateRedcapData(data, redcap_data) @@ -45,4 +33,3 @@ package to use a reference for comparing to expected data structure.} Utilities for recognizing and validating data structures for use with the REDCap API } -\keyword{datasets} From cd7571d74a9bea5dcb0f8d9991411ea8fa2279a2 Mon Sep 17 00:00:00 2001 From: Shawn Garbett Date: Tue, 25 Aug 2026 10:30:25 -0500 Subject: [PATCH 2/2] remove ordering assumption from exportLogging test #517 --- .../testthat/test-800-exportLogging-Functionality.R | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-800-exportLogging-Functionality.R b/tests/testthat/test-800-exportLogging-Functionality.R index ab7fb87c..6391b4f0 100644 --- a/tests/testthat/test-800-exportLogging-Functionality.R +++ b/tests/testthat/test-800-exportLogging-Functionality.R @@ -2,18 +2,22 @@ # Also, this needs to be in the timezone of the server, which ours is CDT # If another user wishes to automate these test this could be an ENV variable. systime <- as.POSIXct(Sys.time(), tz="America/Chicago") -BEGIN_TIME <- as.POSIXct( - format(systime, format = "%Y-%m-%d %H:%M"), tz="America/Chicago") - 7*24*60*60 +CUR_TIME <- as.POSIXct( + format(systime, format = "%Y-%m-%d %H:%M"), tz="America/Chicago") +BEGIN_TIME <- CUR_TIME - 7*24*60*60 RecentLog <- exportLogging(rcon, beginTime=BEGIN_TIME) test_that( "Logs can be batched and match unbatched", { - endTime <- BEGIN_TIME+6*24*60*60-27*60 # End time is 27min less than 6 days to test final boundary + endTime <- CUR_TIME - 15*60 # End time is 15min less than current time BatchedLog <- exportLogging(rcon, beginTime=BEGIN_TIME, endTime=endTime, batchInterval=1) SameLog <- RecentLog[RecentLog$timestamp >= BEGIN_TIME & RecentLog$timestamp < endTime,] - skip_if(nrow(BatchedLog) == 0, "No logs for test window in past") + skip_if(nrow(RecentLog) == 0, "No logs for test window in past") + + BatchedLog <- BatchedLog[order(BatchedLog$timestamp, BatchedLog$action),] + SameLog <- SameLog[ order(SameLog$timestamp, SameLog$action),] expect_equal(BatchedLog$timestamp, SameLog$timestamp) expect_equal(BatchedLog$action, SameLog$action)