From 4eba32c20b62b9c12b43334b6ca66fdd8d58b5b5 Mon Sep 17 00:00:00 2001 From: Florian Mayer Date: Sun, 31 Mar 2024 17:25:32 +0800 Subject: [PATCH] WIP #152: Add entity_list and entity_detail --- DESCRIPTION | 2 +- NAMESPACE | 2 + R/entity_detail.R | 144 ++++++++++++++++++ R/entity_list.R | 104 +++++++++++++ R/entitylist_detail.R | 3 +- R/entitylist_list.R | 8 +- R/isodt_to_local.R | 17 ++- man-roxygen/param-eid.R | 2 + man/entity_detail.Rd | 172 ++++++++++++++++++++++ man/entity_list.Rd | 128 ++++++++++++++++ man/entitylist_detail.Rd | 2 + man/entitylist_download.Rd | 2 + man/entitylist_list.Rd | 10 +- man/isodt_to_local.Rd | 13 +- tests/testthat/test-entity_detail.R | 80 ++++++++++ tests/testthat/test-entity_list.R | 40 +++++ tests/testthat/test-entitylist_download.R | 5 +- 17 files changed, 714 insertions(+), 20 deletions(-) create mode 100644 R/entity_detail.R create mode 100644 R/entity_list.R create mode 100644 man-roxygen/param-eid.R create mode 100644 man/entity_detail.Rd create mode 100644 man/entity_list.Rd create mode 100644 tests/testthat/test-entity_detail.R create mode 100644 tests/testthat/test-entity_list.R diff --git a/DESCRIPTION b/DESCRIPTION index f883a3ce..3e811971 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: ruODK Title: An R Client for the ODK Central API -Version: 1.4.9.9003 +Version: 1.4.9.9004 Authors@R: c(person(given = c("Florian", "W."), family = "Mayer", diff --git a/NAMESPACE b/NAMESPACE index 4d3257ef..d113d195 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,8 @@ export(encryption_key_list) export(enexpr) export(enquo) export(ensym) +export(entity_detail) +export(entity_list) export(entitylist_detail) export(entitylist_download) export(entitylist_list) diff --git a/R/entity_detail.R b/R/entity_detail.R new file mode 100644 index 00000000..dcdee40f --- /dev/null +++ b/R/entity_detail.R @@ -0,0 +1,144 @@ +#' Show details of one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' +#' This function returns the metadata and current data of an Entity. +#' +#' The data and label of an Entity are found in the `current_version` columns +#' under data and label respectively. +#' The `current_version` column contains version-specific metadata fields +#' such as version, baseVersion, details about conflicts that version +#' introduced, etc. More details are available from `entity_versions()`. +#' The Entity also contains top-level system metadata such as `uuid`, +#' `created_at` and `updated_at` timestamps, and `creator_id` (or full creator +#' if extended metadata is requested). +#' +#' ## Conflicts +#' An Entity's metadata also has a conflict field, which indicates the current +#' conflict status of the Entity. The value of a conflict can either be: +#' +#' - null. The Entity does not have conflicting versions. +#' - soft. The Entity has a version that was based on a version other than the +#' version immediately prior to it. (The specified `base_version` was not the +#' latest version on the server.) +#' - hard. The Entity has a version that was based on a version other than the +#' version immediately prior to it. Further, that version updated the same +#' property as another update. +#' +#' If an Entity has a conflict, it can be marked as resolved. +#' After that, the conflict field will be null until a new conflicting version +#' is received. +#' +#' ## Structure +#' The response from ODK Central from this endpoint is irregular and dynamic. +#' `ruODK` preserves the original structure as not to introduce additional +#' complexity. If a use case exists to decompose the original structure further +#' please create a GitHub issue. +#' +#' ## Names +#' Names are cleaned at the top level only. List columns contain original +#' `camelCase` names. +#' +#' ## Authentication +#' `entity_detail()` will fail with incorrect or missing authentication. +#' +#' ## Compatibility +#' This function is supported from ODK Central v2022.3 and will warn if the +#' given `odkc_version` is lower. +#' +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A tibble with exactly one row for each Entity of the given project +#' as per ODK Central API docs. +#' Column names are renamed from ODK's `camelCase` to `snake_case`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' # The current version of the first Entity +#' ev <- en$current_version_version[1] +#' } +entity_detail <- function(pid = get_default_pid(), + did = NULL, + eid = NULL, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = c( + "YmdHMS", + "YmdHMSz", + "Ymd HMS", + "Ymd HMSz", + "Ymd", + "ymd" + ), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid) + + if (is.null(did)) { + ru_msg_abort("entity_detail requires the Entity List name as 'did=\"name\"'.") + } + + if (is.null(eid)) { + ru_msg_abort("entity_detail requires the Entity UUID as 'eid=\"uuid\"'.") + } + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_detail is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/entities/{eid}" + ) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + # purrr::list_transpose() |> + # + # tibble::enframe() |> + # tibble::as_tibble(.name_repair = "universal") |> + # tidyr::pivot_wider(names_from = "name", values_from = "value") |> + # tidyr::unnest_wider("conflict", names_sep = "_") |> + # janitor::clean_names() |> + # tidyr::unnest_wider("current_version", names_sep = "_") |> + janitor::clean_names() + # dplyr::mutate_at( + # dplyr::vars(dplyr::contains("_at")), + # ~ isodt_to_local(., orders = orders, tz = tz) + # ) +} + +# usethis::use_test("entity_detail") # nolint diff --git a/R/entity_list.R b/R/entity_list.R new file mode 100644 index 00000000..b3ae774a --- /dev/null +++ b/R/entity_list.R @@ -0,0 +1,104 @@ +#' List all Entities of a kind. +#' +#' `r lifecycle::badge("maturing")` +#' +#' This function returns a list of the Entities of a kind (belonging to an +#' Entity List or Dataset). +#' Please note that this endpoint only returns metadata of the entities, not the +#' data. If you want to get the data of all entities then please refer to OData +#' Dataset Service +#' +#' You can provide ?deleted=true to get only deleted entities. +#' +#' `entity_list()` will fail with incorrect or missing authentication. +#' +#' This function is supported from ODK Central v2022.3 and will warn if the +#' given odkc_version is lower. +#' +#' @template param-pid +#' @template param-did +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A tibble with exactly one row for each Entity of the given project +#' as per ODK Central API docs. +#' Column names are renamed from ODK's `camelCase` to `snake_case`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = el$name[1]) +#' +#' # The UUID of the first Entity +#' eid <- en$uuid[1] +#' +#' # The current version of the first Entity +#' ev <- en$current_version_version[1] +#' } +entity_list <- function(pid = get_default_pid(), + did = NULL, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = c( + "YmdHMS", + "YmdHMSz", + "Ymd HMS", + "Ymd HMSz", + "Ymd", + "ymd" + ), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid) + + if (is.null(did)) { + ru_msg_abort("entity_list requires the Entity List name as 'did=\"name\"'.") + } + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_list is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/entities" + ) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + purrr::list_transpose() |> + tibble::as_tibble() |> + janitor::clean_names() |> + tidyr::unnest_wider("current_version", names_sep = "_") |> + tidyr::unnest_wider("conflict", names_sep = "_") |> + janitor::clean_names() |> + dplyr::mutate_at( + dplyr::vars(dplyr::contains("_at")), + ~ isodt_to_local(., orders = orders, tz = tz) + ) +} + +# usethis::use_test("entity_list") # nolint \ No newline at end of file diff --git a/R/entitylist_detail.R b/R/entitylist_detail.R index 78a770ee..dcf39001 100644 --- a/R/entitylist_detail.R +++ b/R/entitylist_detail.R @@ -80,8 +80,7 @@ entitylist_detail <- function(pid = get_default_pid(), ) ), httr::add_headers( - "Accept" = "application/json", - "X-Extended-Metadata" = "true" + "Accept" = "application/json" ), httr::authenticate(un, pw), times = retries diff --git a/R/entitylist_list.R b/R/entitylist_list.R index 66dc833a..cd9d1dad 100644 --- a/R/entitylist_list.R +++ b/R/entitylist_list.R @@ -24,9 +24,9 @@ #' @template param-odkcv #' @template param-orders #' @template param-tz -#' @return A tibble with exactly one row for each dataset of the given project -#' as per ODK Central API docs. -#' Column names are renamed from ODK's `camelCase` to `snake_case`. +#' @return A tibble with exactly one row for each Entity List of the given +#' Project as per ODK Central API docs. +#' Column names are renamed from ODK Central's `camelCase` to `snake_case`. # nolint start #' @seealso \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} # nolint end @@ -35,7 +35,7 @@ #' @examples #' \dontrun{ #' # See vignette("setup") for setup and authentication options -#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' # ruODK::ru_setup(svc = "... .svc", un = "me@email.com", pw = "...") #' #' ds <- entitylist_list(pid = get_default_pid()) #' diff --git a/R/isodt_to_local.R b/R/isodt_to_local.R index 5fdad363..8960c1b2 100644 --- a/R/isodt_to_local.R +++ b/R/isodt_to_local.R @@ -2,22 +2,29 @@ #' #' `r lifecycle::badge("stable")` #' -#' This function is used internally by \code{ruODK} to parse ISO timestamps +#' This function is used internally by `ruODK` to parse ISO timestamps #' to timezone-aware local times. #' +#' Warnings are suppressed through `lubridate::parse_date_time(quiet=TRUE)`. +#' #' @param datetime_string (character) An ISO8601 datetime string as produced by #' XForms exported from ODK Central. #' @param orders (vector of character) Orders of datetime elements for -#' lubridate. +#' `lubridate`. #' Default: \code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz")}. #' @template param-tz -#' @return A lubridate PosixCT datetime in the given timezone. +#' @param quiet (bool) Used in `lubridate::parse_date_time(quiet=quiet)` to +#' suppress warnings from attempting to parse all empty values or columns. +#' Run with `quiet=FALSE` to show any `lubridate` warnings. +#' @return A `lubridate` PosixCT datetime in the given timezone. #' @family utilities #' @keywords internal isodt_to_local <- function(datetime_string, orders = c("YmdHMS", "YmdHMSz"), - tz = get_default_tz()) { + tz = get_default_tz(), + quiet=TRUE + ) { datetime_string %>% - lubridate::parse_date_time(orders = orders) %>% + lubridate::parse_date_time(orders = orders, quiet=quiet) %>% lubridate::with_tz(., tzone = tz) } diff --git a/man-roxygen/param-eid.R b/man-roxygen/param-eid.R new file mode 100644 index 00000000..d27ad6c4 --- /dev/null +++ b/man-roxygen/param-eid.R @@ -0,0 +1,2 @@ +#' @param eid (chr) The UUID of an Entity, which can be retrieved by +#' `entity_list()`. diff --git a/man/entity_detail.Rd b/man/entity_detail.Rd new file mode 100644 index 00000000..4b5fc9b5 --- /dev/null +++ b/man/entity_detail.Rd @@ -0,0 +1,172 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_detail.R +\name{entity_detail} +\alias{entity_detail} +\title{Show details of one Entity.} +\usage{ +entity_detail( + pid = get_default_pid(), + did = NULL, + eid = NULL, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset.} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}.} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A tibble with exactly one row for each Entity of the given project +as per ODK Central API docs. +Column names are renamed from ODK's \code{camelCase} to \code{snake_case}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This function returns the metadata and current data of an Entity. + +The data and label of an Entity are found in the \code{current_version} columns +under data and label respectively. +The \code{current_version} column contains version-specific metadata fields +such as version, baseVersion, details about conflicts that version +introduced, etc. More details are available from \code{entity_versions()}. +The Entity also contains top-level system metadata such as \code{uuid}, +\code{created_at} and \code{updated_at} timestamps, and \code{creator_id} (or full creator +if extended metadata is requested). +\subsection{Conflicts}{ + +An Entity's metadata also has a conflict field, which indicates the current +conflict status of the Entity. The value of a conflict can either be: +\itemize{ +\item null. The Entity does not have conflicting versions. +\item soft. The Entity has a version that was based on a version other than the +version immediately prior to it. (The specified \code{base_version} was not the +latest version on the server.) +\item hard. The Entity has a version that was based on a version other than the +version immediately prior to it. Further, that version updated the same +property as another update. +} + +If an Entity has a conflict, it can be marked as resolved. +After that, the conflict field will be null until a new conflicting version +is received. +} + +\subsection{Structure}{ + +The response from ODK Central from this endpoint is irregular and dynamic. +\code{ruODK} preserves the original structure as not to introduce additional +complexity. If a use case exists to decompose the original structure further +please create a GitHub issue. +} + +\subsection{Names}{ + +Names are cleaned at the top level only. List columns contain original +\code{camelCase} names. +} + +\subsection{Authentication}{ + +\code{entity_detail()} will fail with incorrect or missing authentication. +} + +\subsection{Compatibility}{ + +This function is supported from ODK Central v2022.3 and will warn if the +given \code{odkc_version} is lower. +} +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +# The current version of the first Entity +ev <- en$current_version_version[1] +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} + +Other entity-management: +\code{\link{entity_list}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()} +} +\concept{entity-management} diff --git a/man/entity_list.Rd b/man/entity_list.Rd new file mode 100644 index 00000000..b9c7c482 --- /dev/null +++ b/man/entity_list.Rd @@ -0,0 +1,128 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_list.R +\name{entity_list} +\alias{entity_list} +\title{List all Entities of a kind.} +\usage{ +entity_list( + pid = get_default_pid(), + did = NULL, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset.} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A tibble with exactly one row for each Entity of the given project +as per ODK Central API docs. +Column names are renamed from ODK's \code{camelCase} to \code{snake_case}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This function returns a list of the Entities of a kind (belonging to an +Entity List or Dataset). +Please note that this endpoint only returns metadata of the entities, not the +data. If you want to get the data of all entities then please refer to OData +Dataset Service + +You can provide ?deleted=true to get only deleted entities. + +\code{entity_list()} will fail with incorrect or missing authentication. + +This function is supported from ODK Central v2022.3 and will warn if the +given odkc_version is lower. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + + el <- entitylist_list() + + # Entity List name (dataset ID) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = el$name[1]) + + # The UUID of the first Entity + eid <- en$uuid[1] + + # The current version of the first Entity + ev <- en$current_version_version[1] +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} + +Other entity-management: +\code{\link{entity_detail}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()} +} +\concept{entity-management} diff --git a/man/entitylist_detail.Rd b/man/entitylist_detail.Rd index b863bb6c..6d8f9bfe 100644 --- a/man/entitylist_detail.Rd +++ b/man/entitylist_detail.Rd @@ -115,6 +115,8 @@ ds1$properties |> \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, \code{\link{entitylist_download}()}, \code{\link{entitylist_list}()} } diff --git a/man/entitylist_download.Rd b/man/entitylist_download.Rd index 75b43fa9..83ad608a 100644 --- a/man/entitylist_download.Rd +++ b/man/entitylist_download.Rd @@ -188,6 +188,8 @@ ds3 <- entitylist_download( \url{https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, \code{\link{entitylist_detail}()}, \code{\link{entitylist_list}()} } diff --git a/man/entitylist_list.Rd b/man/entitylist_list.Rd index 560d5a10..b00c2a8f 100644 --- a/man/entitylist_list.Rd +++ b/man/entitylist_list.Rd @@ -72,9 +72,9 @@ Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s timezone can be set globally or per function.} } \value{ -A tibble with exactly one row for each dataset of the given project -as per ODK Central API docs. -Column names are renamed from ODK's \code{camelCase} to \code{snake_case}. +A tibble with exactly one row for each Entity List of the given +Project as per ODK Central API docs. +Column names are renamed from ODK Central's \code{camelCase} to \code{snake_case}. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} @@ -98,7 +98,7 @@ given odkc_version is lower. \examples{ \dontrun{ # See vignette("setup") for setup and authentication options -# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +# ruODK::ru_setup(svc = "... .svc", un = "me@email.com", pw = "...") ds <- entitylist_list(pid = get_default_pid()) @@ -109,6 +109,8 @@ ds |> knitr::kable() \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, \code{\link{entitylist_detail}()}, \code{\link{entitylist_download}()} } diff --git a/man/isodt_to_local.Rd b/man/isodt_to_local.Rd index 15ba65a3..a627e3fa 100644 --- a/man/isodt_to_local.Rd +++ b/man/isodt_to_local.Rd @@ -7,7 +7,8 @@ isodt_to_local( datetime_string, orders = c("YmdHMS", "YmdHMSz"), - tz = get_default_tz() + tz = get_default_tz(), + quiet = TRUE ) } \arguments{ @@ -15,16 +16,20 @@ isodt_to_local( XForms exported from ODK Central.} \item{orders}{(vector of character) Orders of datetime elements for -lubridate. +\code{lubridate}. Default: \code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz")}.} \item{tz}{A timezone to convert dates and times to. Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s timezone can be set globally or per function.} + +\item{quiet}{(bool) Used in \code{lubridate::parse_date_time(quiet=quiet)} to +suppress warnings from attempting to parse all empty values or columns. +Run with \code{quiet=FALSE} to show any \code{lubridate} warnings.} } \value{ -A lubridate PosixCT datetime in the given timezone. +A \code{lubridate} PosixCT datetime in the given timezone. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} @@ -32,6 +37,8 @@ A lubridate PosixCT datetime in the given timezone. \details{ This function is used internally by \code{ruODK} to parse ISO timestamps to timezone-aware local times. + +Warnings are suppressed through \code{lubridate::parse_date_time(quiet=TRUE)}. } \seealso{ Other utilities: diff --git a/tests/testthat/test-entity_detail.R b/tests/testthat/test-entity_detail.R new file mode 100644 index 00000000..ba8bfb32 --- /dev/null +++ b/tests/testthat/test-entity_detail.R @@ -0,0 +1,80 @@ +test_that("entity_detail works", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = el$name[1]) + + # Entity detail + ed <- entity_detail(did = el$name[1], eid = en$uuid[1]) + + cn <- c( + "uuid", + "created_at", + "creator_id", + "updated_at", + "deleted_at", + "conflict", + "current_version" + # not unfolded: + # "current_version_created_at", + # "current_version_current", + # "current_version_label", + # "current_version_creator_id", + # "current_version_user_agent", + # "current_version_version", + # "current_version_base_version", + # "current_version_conflicting_properties" + ) + testthat::expect_equal(cn, names(ed)) + + # The UUID of the first Entity + eid <- en$uuid[1] + testthat::expect_is(eid, "character") + + # The current version of the first Entity + ev <- en$current_version_version[1] + testthat::expect_is(ev, "integer") +}) + +test_that("entitylist_detail errors if did is missing", { + testthat::expect_error( + entitylist_detail() + ) +}) + +test_that("entitylist_detail warns if odkc_version too low", { + skip_if(Sys.getenv("ODKC_TEST_URL") == "", + message = "Test server not configured" + ) + + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + ds <- entitylist_list() + did <- ds$name[1] + + ds1 <- entitylist_detail(did = did) + + testthat::expect_warning( + ds1 <- entitylist_detail(did = did, odkc_version = "1.5.3") + ) +}) + + +# usethis::use_e("entity_detail") # nolint \ No newline at end of file diff --git a/tests/testthat/test-entity_list.R b/tests/testthat/test-entity_list.R new file mode 100644 index 00000000..522eff18 --- /dev/null +++ b/tests/testthat/test-entity_list.R @@ -0,0 +1,40 @@ +test_that("entity_list works", { + skip_if(Sys.getenv("ODKC_TEST_URL") == "", + message = "Test server not configured" + ) + + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + did <- el$name[1] + en <- entity_list(did = el$name[1]) + eid <- en$uuid[1] + + testthat::expect_s3_class(en, "tbl_df") + + cn <- c( + "uuid", + "created_at", + "creator_id", + "updated_at", + "deleted_at", + "current_version_created_at", + "current_version_current", + "current_version_label", + "current_version_creator_id", + "current_version_user_agent", + "current_version_version", + "current_version_base_version", + "current_version_conflicting_properties" + ) + + testthat::expect_equal(names(en), cn) +}) + +# usethis::use_r("entity_list") # nolint diff --git a/tests/testthat/test-entitylist_download.R b/tests/testthat/test-entitylist_download.R index f0281f91..9c5613f4 100644 --- a/tests/testthat/test-entitylist_download.R +++ b/tests/testthat/test-entitylist_download.R @@ -122,7 +122,7 @@ test_that("entitylist_download filter works", { newest_entity_date <- as.Date(max(ds1$entities$`__createdAt`)) # Should return all entities (created before or on date of latest entity) - # Currently returns HTTP 501 not implemented + # Currently returns HTTP 501 # ds2 <- entitylist_download( # did = ds$name[1], # filter=glue::glue("__createdAt le {newest_entity_date}") @@ -130,6 +130,9 @@ test_that("entitylist_download filter works", { # testthat::expect_equal(ds2$http_status, 200) # testthat::expect_true(nrow(ds2$entities)) + + # Resolve warning about empty test + testthat::expect_true(TRUE) }) test_that("entitylist_download errors if did is missing", {