From 8d59925033ea77efd78bacce7d190f5057224fbd Mon Sep 17 00:00:00 2001 From: Gregor de Cillia Date: Fri, 9 Dec 2022 14:15:27 +0100 Subject: [PATCH] add typechecks to sc_table_custom() there are now several checks in place that throw warnings if inputs in sc_table_custom() or sc_recode() are of the wrong schema-type or if other inconsistencies are suspected. See the section called "error handling" in ?sc_table_custom for more details some of those warnings might be replaced with errors in the future part of #33 --- R/table_custom.R | 44 +++++++++++++++++++++++++++++++++++++----- man/sc_table_custom.Rd | 20 +++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/R/table_custom.R b/R/table_custom.R index 6fceb56e..2d8cc704 100644 --- a/R/table_custom.R +++ b/R/table_custom.R @@ -24,6 +24,21 @@ #' it is possible to pass `sc_schema` objects (usually generated by #' [sc_schema_db()]) instead of ids in [sc_table_custom()] and [sc_recode()]. #' If provided, the schema objects will be converted into ids via `$id`. +#' @section Error handling: +#' Unfortunately, the API gives fairly vague error messages in case a +#' custom table request is ill defined. For this reason, [sc_table_custom()] +#' applies some simple heuristics and throws warnings if inconsistencies +#' in the provided parameters are recognized. The following conditions are +#' currently checked +#' * the parameter `db` is of type `DATABASE` +#' * all entries in `measures` are of type `MEASURE`, `COUNT` or +#' `STATFN` +#' * all entries in `dimensions` are of type `VALUESET` or `FIELD` +#' * all entries in `field` are of type `VALUESET` or `FIELD` +#' * all entries in `map` are of type `VALUE` +#' * all fields in `recodes` are also present in `dimensions` +#' * the first two arguments of `sc_recode()` are consistent, i.e. +#' if the provided `VALUE`s belong to the `VALUESET/FIELD` #' @examples #' sc_table_custom("str:database:detouextregsai") #' @@ -66,15 +81,25 @@ sc_table_custom <- function(db, measures = c(), dimensions = c(), language = c("en", "de"), add_totals = TRUE, key = NULL, recodes = NULL) { - json_list <- list(database = as_id(db), measures = as.list(as_id(measures, TRUE)), - dimensions = lapply(as_id(dimensions, TRUE), list)) + db <- as_id(db) + measures <- as_id(measures, TRUE) + dimensions <- as_id(dimensions, TRUE) + json_list <- list(database = db, measures = as.list(measures), + dimensions = lapply(dimensions, list)) if (!is.null(recodes)) { json_list$recodes <- recodes add_totals <- FALSE } - #return(jsonlite::toJSON(json_list, auto_unbox = TRUE, pretty = TRUE)) json <- jsonlite::toJSON(json_list, auto_unbox = TRUE, pretty = TRUE) - #return(json) + if (!all(names(recodes) %in% dimensions)) + warning("`recodes` and `dimensions` might be inconsistent") + if (!all(grepl("^str:valueset", dimensions) | grepl("^str:field", dimensions))) + warning("parameter `dimensions` is not of type `FIELD` or `VALUESET`") + if (!all(grepl("^str:measure", measures) | grepl("^str:statfn", measures) | + grepl("^str:count", measures))) + warning("parameter `measures` is not of type `MEASURE`, `STATFN` or `COUNT`") + if (!grepl("^str:database", db)) + warning("parameter `db` is not of type `DATABASE`") response <- sc_table_json_post(json, language, add_totals, key) sc_table_class$new(response, toString(json)) } @@ -94,7 +119,7 @@ sc_recode <- function(field, map = NULL, total = FALSE) { return(stats::setNames(list(list(total = total)), as_id(field))) if (inherits(map, "sc_schema")) map <- list(map) - stats::setNames( + recode <- stats::setNames( list(list( map = lapply(map, function(value) { I(as_id(value, TRUE)) @@ -103,6 +128,15 @@ sc_recode <- function(field, map = NULL, total = FALSE) { )), as_id(field) ) + code_parent <- gsub("^.*:", "", names(recode)) + codes_children <- unlist(recode[[1]]$map) + if (!all(grepl(code_parent, codes_children))) + warning("parameters `field` and `map` might be inconsistent") + if (!all(grepl("^str:value:", codes_children))) + warning("some entries in `map` are not of type VALUE") + if (!grepl("^str:valueset", names(recode)) && !grepl("^str:field", names(recode))) + warning("parameter `field` is not of type `FIELD` or `VALUESET`") + recode } as_id <- function(x, multiple = FALSE) { diff --git a/man/sc_table_custom.Rd b/man/sc_table_custom.Rd index e1ccc936..940e2745 100644 --- a/man/sc_table_custom.Rd +++ b/man/sc_table_custom.Rd @@ -70,6 +70,26 @@ it is possible to pass \code{sc_schema} objects (usually generated by If provided, the schema objects will be converted into ids via \verb{$id}. } +\section{Error handling}{ + +Unfortunately, the API gives fairly vague error messages in case a +custom table request is ill defined. For this reason, \code{\link[=sc_table_custom]{sc_table_custom()}} +applies some simple heuristics and throws warnings if inconsistencies +in the provided parameters are recognized. The following conditions are +currently checked +\itemize{ +\item the parameter \code{db} is of type \code{DATABASE} +\item all entries in \code{measures} are of type \code{MEASURE}, \code{COUNT} or +\code{STATFN} +\item all entries in \code{dimensions} are of type \code{VALUESET} or \code{FIELD} +\item all entries in \code{field} are of type \code{VALUESET} or \code{FIELD} +\item all entries in \code{map} are of type \code{VALUE} +\item all fields in \code{recodes} are also present in \code{dimensions} +\item the first two arguments of \code{sc_recode()} are consistent, i.e. +if the provided \code{VALUE}s belong to the \code{VALUESET/FIELD} +} +} + \examples{ sc_table_custom("str:database:detouextregsai")