R/kobo_audit.R

Defines functions kobo_audit.default kobo_audit.character kobo_audit.kobo_asset kobo_audit kobo_audit_

Documented in kobo_audit

#' @noRd
kobo_audit_ <- function(uid, progress = FALSE) {
  audit_meta <- get_audit_url_(uid)
  headers <- list(Authorization = paste("Token",
                                        Sys.getenv("KOBOTOOLBOX_TOKEN")))
  reqs <- lapply(audit_meta$download_url, function(url) {
    req <- HttpRequest$new(url,
                           headers = headers)
    req$retry("get",
              times = 3L,
              retry_only_on = c(500, 502, 503),
              terminate_on = 404)
  })
  sleep <- 0.05
  res <- AsyncQueue$new(.list = reqs,
                        bucket_size = Inf,
                        sleep = sleep)
  res$request()
  cond <- res$status_code() >= 300L
  if (any(cond)) {
    msg <- res$content()[cond]
    abort(error_msg(msg[[1]]),
          call = NULL)
  }

  if (isTRUE(progress))
    cli_progress_step("Processing audit data")

  res <- res$parse(encoding = "UTF-8")
  col_classes <- c("event",
                   "node",
                   "old-value",
                   "new-value",
                   "user",
                   "change-reason")
  col_classes <- list(character = col_classes)
  res <- mutate(audit_meta,
                data = lapply(res, \(path) suppressWarnings(fread(path,
                                                                  colClasses = col_classes,
                                                                  data.table = FALSE))))

  res <- select(res, -"download_url")
  res <- unnest(res, "data")
  res |>
    mutate(name = basename(.data$node), .before = "start",
           start_int = .data$start,
           start = as.POSIXct(.data$start_int/1000, origin = "1970-01-01"),
           end_int = .data$end,
           end = as.POSIXct(.data$end_int/1000, origin = "1970-01-01"))
}

#' Get all audit logs data from a KoboToolbox survey
#'
#' Get all audit logs data from a KoboToolbox survey through a \code{kobo_asset} or
#' asset unique identifier.
#'
#' @name kobo_audit
#'
#' @importFrom RcppSimdJson fparse
#' @importFrom purrr list_rbind
#' @importFrom dplyr mutate select
#' @importFrom tidyr unnest
#' @importFrom data.table fread
#' @importFrom readr type_convert
#'
#' @param x the unique identifier of a specific asset (`character`) or
#' a \code{kobo_asset} object.
#' @param progress logical, whether or not you want to see the progess via message.
#' Default to `FALSE`.
#'
#' @returns A \code{data.frame}. It contains survey paradata from audit logs.
#' The following columns are available:
#'
#' - `_id`  This columns generated by `robotoolbox` allow you to do a mapping
#' the `_id` of the submissions in `kobo_data`.
#'
#' - `event` the action that took place. The different event types include.
#' form start, form exit, question, group questions, end screen, and device
#' or metadata audit.
#'
#' - `node` the name of the question or group related to the event.
#'
#' - `name` This column is appended by `robotoolbox` to match the name of the question
#' in the audit and the data from `kobo_data`.
#'
#' - `start` the timestamp when the event started.
#'
#' - `end` the timestamp when the event ended.
#'
#' - `latitude` the latitude of the device when the event occurred.
#'
#' - `longitude` the longitude of the device when the event occurred.
#'
#' - `accuracy` the GPS accuracy of the location data.
#'
#' - `old-value` the previous value of the question before it was changed in this event.
#'
#' - `new-value` the new value of the question after it was changed in this event.
#'
#' - `user` the username of the data collector.
#'
#' - `change-reason` the reason before they save changes to a form.
#'
#' @examples
#' \dontrun{
#' kobo_setup()
#' uid <- "a9cwEQcbWqWzdA5eqkjRUWi"
#' asset <- kobo_asset(uid)
#' audit <- kobo_audit(asset)
#'
#' if (require(dplyr)) {
#'  library(dplyr)
#'  glimpse(audit)
#'  }
#' }
#'
#' @export
kobo_audit <- function(x, progress) {
  UseMethod("kobo_audit")
}

#' @importFrom purrr list_rbind
#' @importFrom dplyr filter
#' @importFrom rlang abort
#' @export
kobo_audit.kobo_asset <- function(x, progress = FALSE) {
  if (isTRUE(progress))
    cli_progress_step("Checking audit data availability")
  asset_version_list <- kobo_asset_version_list(x$uid)
  asset_version_list <- filter(asset_version_list,
                               .data$deployed)
  cond <- nrow(asset_version_list) > 0
  if (cond) {
    version <- unique(asset_version_list$uid)
    form <- lapply(version, \(v) kobo_form(x, v))
    form <- dt2tibble(rbindlist(form, fill = TRUE))
  } else {
    form <- kobo_form(x)
  }

  if (!any("audit" %in% form$name))
    abort("`audit` not enabled in the current version of the survey",
          call = NULL)
  if (isTRUE(progress))
    cli_progress_step("Downloading audit data")
  kobo_audit_(x$uid, progress = progress)
}

#' @export
kobo_audit.character <- function(x, progress = FALSE) {
  if (!assert_uid(x))
    abort(message = "Invalid asset uid")
  kobo_audit(kobo_asset(x), progress = progress)
}

#' @export
kobo_audit.default <- function(x, progress) {
  abort("You need to use a 'kobo_asset' or an asset uid")
}
dickoa/robotoolbox documentation built on July 12, 2024, 1:55 p.m.