diff --git a/DESCRIPTION b/DESCRIPTION index 2a52242..2130d92 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: eia Title: API Wrapper for 'US Energy Information Administration' Open Data -Version: 0.4.0 +Version: 0.4.1 Authors@R: c(person( given = "Matthew", diff --git a/NEWS.md b/NEWS.md index 98720ac..6b09f44 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,14 @@ # eia 0.4.1 -* Added dynamic error handling to `eia_data()` via metadata layer conditioned on -new function argument `check_metadata`. - +* Re-factor of `eia_data()`: + * Added dynamic error handling via metadata layer conditioned on new function argument `check_metadata`. + * Forced `start` and `end` input values to character. + * Forced `length` and `offset` input values to numeric. + * Augmented `sort` functionality to now handle multiple, varying "asc"/"desc" inputs + to `order` as a list, rather than just one value of "asc" or "desc" applied to all + specified columns. + * Improved error messaging with `check_metadata = TRUE` to more closely resemble + those provided by the API (i.e. with `check_metadata = FALSE`). # eia 0.4.0 diff --git a/R/data.R b/R/data.R index 9278b51..50ea4ab 100644 --- a/R/data.R +++ b/R/data.R @@ -6,6 +6,14 @@ #' input values for each of these arguments, use the specific ID labels #' as provided by `eia_metadata()`. #' +#' The use of `start` and `end` require some input to `freq`. +#' By default (`check_metadata = FALSE`), the resulting data will match the +#' temporal resolution provided to `freq`, however, `check_metadata = TRUE` applies +#' further restrictions such that the format of values provided to `start`/`end` must match +#' that of `freq`. Furthermore, regardless of the input format provided to `start`/`end`, +#' the resulting data will always match the specification of `freq`. And lastly, +#' regardless of chosen format, `end` must be strictly greater than `start` to return data. +#' #' By default, additional processing is done to return a list containing tibble data frames. #' Set `tidy = FALSE` to return only the initial list result of `jsonlite::fromJSON`. #' Set `tidy = NA` to return the original JSON as a character string. @@ -20,8 +28,7 @@ #' @param data character or `NULL`, see details. #' @param facets character list or `NULL`, see details. #' @param freq character or `NULL`, see details. -#' @param start,end character, integer or `NULL`, must match format of default or supplied -#' `freq`; e.g. if `freq = "yearly"`, then format must be `YYYY`. +#' @param start,end character or `NULL`, see details. #' @param sort named list of two. #' * `cols`: list column names on which to sort. #' * `order`: `"asc"` or `"desc"` for ascending or descending, respectively. @@ -71,7 +78,7 @@ eia_data <- function(dir, warning(wrngs, "\nTotal available rows: ", r$response$total, call. = FALSE) } else { if (r$response$total == 0) - stop("No data available - check inputs.", call. = FALSE) + stop("No data available - check temporal inputs.", call. = FALSE) if (nrow(r$response$data) != r$response$total) warning("Rows returned: ", nrow(r$response$data), "\nRows available: ", r$response$total, call. = FALSE) } @@ -153,6 +160,8 @@ eia_data <- function(dir, # Start input formatting and validation .start_specs <- function(start, freq){ if(!is.null(start)){ + if(!is.character(start)) + stop("'start' must be a character string of length 1.", call. = FALSE) if(is.null(freq)) stop("'start' requires 'freq' be non-NULL.", call. = FALSE) paste0("&start=", start) @@ -161,11 +170,13 @@ eia_data <- function(dir, .start_check <- function(start, freq, md_frq_tbl, mds, mde){ if(!is.null(start)){ + if(!is.character(start)) + stop("'start' must be a character string of length 1.", call. = FALSE) if (is.null(freq)) stop("'start' requires 'freq' be non-NULL.", call. = FALSE) fmt <- md_frq_tbl[md_frq_tbl$id == freq, ]$format if (nchar(start) != nchar(fmt)) - stop("'start' must be a string of format: ", fmt, call. = FALSE) + stop("'start' must be a character string of format: ", fmt, call. = FALSE) if (start > mde) stop("'start' is beyond the end of available data.", call. = FALSE) if (start < mds) @@ -176,6 +187,8 @@ eia_data <- function(dir, # End input formatting and validation .end_specs <- function(end, freq){ if (!is.null(end)){ + if(!is.character(end)) + stop("'end' must be a character string of length 1.", call. = FALSE) if(is.null(freq)) stop("'end' requires 'freq' be non-NULL.", call. = FALSE) paste0("&end=", end) @@ -184,11 +197,13 @@ eia_data <- function(dir, .end_check <- function(end, freq, md_frq_tbl, mde, mds){ if (!is.null(end)){ + if(!is.character(end)) + stop("'end' must be a character string of length 1.", call. = FALSE) if (is.null(freq)) stop("'end' requires 'freq' be non-NULL.", call. = FALSE) fmt <- md_frq_tbl[md_frq_tbl$id == freq, ]$format if (nchar(end) != nchar(fmt)) - stop("'end' must be a string of format: ", fmt, call. = FALSE) + stop("'end' must be a character string of format: ", fmt, call. = FALSE) if (end < mds) stop("'end' is before the start of available data.", call. = FALSE) if (end > mde) @@ -224,8 +239,8 @@ eia_data <- function(dir, # Length input formatting and validation .lng_specs <- function(length){ if (!is.null(length)){ - if (length > 5000 | length < 0) - stop("'length' must be a single value between 0 and 5000.", call. = FALSE) + if (!is.numeric(length) | length > 5000 | length < 0) + stop("'length' must be a numeric value between 0 and 5000.", call. = FALSE) paste0("&length=", length) } } @@ -233,8 +248,8 @@ eia_data <- function(dir, # Offset input formatting and validation .ofs_specs <- function(offset){ if (!is.null(offset)){ - if (offset < 0) - stop("'offset' must be a single value greater than 0.", call. = FALSE) + if (!is.numeric(offset) | offset < 0) + stop("'offset' must be a numeric value greater than 0.", call. = FALSE) paste0("&offset=", offset) } } diff --git a/codemeta.json b/codemeta.json index 57fc250..cf9b46c 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,7 +8,7 @@ "codeRepository": "https://github.com/ropensci/eia", "issueTracker": "https://github.com/ropensci/eia/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.4.0", + "version": "0.4.1", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", @@ -229,5 +229,5 @@ }, "SystemRequirements": null }, - "fileSize": "191.382KB" + "fileSize": "202.642KB" } diff --git a/cran-comments.md b/cran-comments.md index f5bb356..0efa584 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -8,8 +8,7 @@ ## Update release -* This update includes a maintainer change/email address update. -* Refactored package to work with newer version 2 of the US Energy Information Administration API. +* Code updates and additional unit testing. ## R CMD check results diff --git a/man/eia_data.Rd b/man/eia_data.Rd index 03e1a09..12b3c35 100644 --- a/man/eia_data.Rd +++ b/man/eia_data.Rd @@ -29,8 +29,7 @@ eia_data( \item{freq}{character or \code{NULL}, see details.} -\item{start, end}{character, integer or \code{NULL}, must match format of default or supplied -\code{freq}; e.g. if \code{freq = "yearly"}, then format must be \code{YYYY}.} +\item{start, end}{character or \code{NULL}, see details.} \item{sort}{named list of two. \itemize{ @@ -63,6 +62,14 @@ By default, \code{data}, \code{facets}, and \code{freq} are set to \code{NULL}. input values for each of these arguments, use the specific ID labels as provided by \code{eia_metadata()}. +The use of \code{start} and \code{end} require some input to \code{freq}. +By default (\code{check_metadata = FALSE}), the resulting data will match the +temporal resolution provided to \code{freq}, however, \code{check_metadata = TRUE} applies +further restrictions such that the format of values provided to \code{start}/\code{end} must match +that of \code{freq}. Furthermore, regardless of the input format provided to \code{start}/\code{end}, +the resulting data will always match the specification of \code{freq}. And lastly, +regardless of chosen format, \code{end} must be strictly greater than \code{start} to return data. + By default, additional processing is done to return a list containing tibble data frames. Set \code{tidy = FALSE} to return only the initial list result of \code{jsonlite::fromJSON}. Set \code{tidy = NA} to return the original JSON as a character string. diff --git a/revdep/README.md b/revdep/README.md index 28a5588..19cd376 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -10,7 +10,7 @@ |collate |English_United States.utf8 | |ctype |English_United States.utf8 | |tz |America/Denver | -|date |2023-10-31 | +|date |2023-11-17 | |rstudio |2023.06.1+524 Mountain Hydrangea (desktop) | |pandoc |3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown) | @@ -18,7 +18,7 @@ |package |old |new |Δ | |:-----------|:-----|:-----|:--| -|eia |0.3.7 |0.4.0 |* | +|eia |0.4.0 |0.4.1 |* | |askpass |1.2.0 |1.2.0 | | |cachem |1.0.8 |1.0.8 | | |cellranger |1.1.0 |1.1.0 | | @@ -26,7 +26,6 @@ |cpp11 |0.4.6 |0.4.6 | | |crayon |1.5.2 |1.5.2 | | |curl |5.1.0 |5.1.0 | | -|dplyr |1.1.3 |NA |* | |fansi |1.0.5 |1.0.5 | | |fastmap |1.1.1 |1.1.1 | | |generics |0.1.3 |0.1.3 | | @@ -34,7 +33,7 @@ |hms |1.1.3 |1.1.3 | | |httr |1.4.7 |1.4.7 | | |jsonlite |1.8.7 |1.8.7 | | -|lifecycle |1.0.3 |1.0.3 | | +|lifecycle |1.0.4 |1.0.4 | | |lubridate |1.9.3 |1.9.3 | | |magrittr |2.0.3 |2.0.3 | | |memoise |2.0.1 |2.0.1 | | @@ -48,14 +47,12 @@ |R6 |2.5.1 |2.5.1 | | |readxl |1.4.3 |1.4.3 | | |rematch |2.0.0 |2.0.0 | | -|rlang |1.1.1 |1.1.1 | | +|rlang |1.1.2 |1.1.2 | | |sys |3.4.2 |3.4.2 | | |tibble |3.2.1 |3.2.1 | | -|tidyselect |1.2.0 |NA |* | |timechange |0.2.0 |0.2.0 | | |utf8 |1.2.4 |1.2.4 | | |vctrs |0.6.4 |0.6.4 | | -|withr |2.5.2 |NA |* | # Revdeps diff --git a/tests/testthat/test-data.R b/tests/testthat/test-data.R index 22e4a6a..bac05cc 100644 --- a/tests/testthat/test-data.R +++ b/tests/testthat/test-data.R @@ -19,7 +19,7 @@ test_that("data queries return data as expected", { dir = "electricity/retail-sales", data = "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", start = 2011, end = 2020, + freq = "annual", start = "2011", end = "2020", sort = list(cols = "period", order = "asc") ) expect_s3_class(x, "tbl_df") @@ -41,7 +41,7 @@ test_that("data queries return data as expected", { dir = "electricity/retail-sales", data = "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", start = 2011, end = 2020, + freq = "annual", start = "2011", end = "2020", sort = list(cols = "period", order = "asc"), length = 8 ), @@ -87,59 +87,87 @@ test_that("'start' and 'end' error/warning messages return as expected", { # Test "start" input value err <- "'start' requires 'freq' be non-NULL." - expect_error(eia_data("electricity/retail-sales", start = 2020), err) - expect_error(eia_data("electricity/retail-sales", start = 2020, check_metadata = TRUE), err) - expect_no_error(eia_data("electricity/retail-sales", freq = "annual", start = 2020)) - err <- "No data available - check inputs." - expect_error(eia_data("electricity/retail-sales", freq = "annual", start = 2099), err) + expect_error(eia_data("electricity/retail-sales", start = "2020"), err) + expect_error(eia_data("electricity/retail-sales", start = "2020", check_metadata = TRUE), err) + err <- "'start' must be a character string of length 1." + expect_error(eia_data("electricity/retail-sales", freq = "annual", start = 2020), err) + expect_error(eia_data("electricity/retail-sales", freq = "annual", start = 2020, check_metadata = TRUE), err) + expect_no_error(eia_data("electricity/retail-sales", freq = "annual", start = "2020")) + err <- "No data available - check temporal inputs." + expect_error(eia_data("electricity/retail-sales", freq = "annual", start = "2099"), err) err <- "'start' is beyond the end of available data." - expect_error(eia_data("electricity/retail-sales", freq = "annual", start = 2099, check_metadata = TRUE), err) - eia_clear_data() expect_error(eia_data("electricity/retail-sales", freq = "annual", start = "2099", check_metadata = TRUE), err) expect_no_error(eia_data("electricity/retail-sales", freq = "annual", start = "2020-06")) - err <- "'start' must be a string of format: YYYY" + err <- "'start' must be a character string of format: YYYY" expect_error(eia_data("electricity/retail-sales", freq = "annual", start = "2020-06", check_metadata = TRUE), err) - err <- "'start' must be a string of format: YYYY-MM" - expect_error(eia_data("electricity/retail-sales", freq = "monthly", start = 2020, check_metadata = TRUE), err) - eia_clear_data() + err <- "'start' must be a character string of format: YYYY-MM" expect_error(eia_data("electricity/retail-sales", freq = "monthly", start = "2020", check_metadata = TRUE), err) wrn <- "'start' is beyond available history. Earliest available: 2001-01" expect_warning( eia_data( "electricity/retail-sales", "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", start = 1980, + freq = "annual", start = "1980", check_metadata = TRUE ) ) # Test "end" input value err <- "'end' requires 'freq' be non-NULL." - expect_error(eia_data("electricity/retail-sales", end = 2001), err) - expect_error(eia_data("electricity/retail-sales", end = 2001, check_metadata = TRUE), err) - expect_no_error(eia_data("electricity/retail-sales", freq = "annual", end = 2001)) - err <- "No data available - check inputs." - expect_error(eia_data("electricity/retail-sales", freq = "annual", end = 1980), err) + expect_error(eia_data("electricity/retail-sales", end = "2001"), err) + expect_error(eia_data("electricity/retail-sales", end = "2001", check_metadata = TRUE), err) + err <- "'end' must be a character string of length 1." + expect_error(eia_data("electricity/retail-sales", freq = "annual", end = 2001), err) + expect_error(eia_data("electricity/retail-sales", freq = "annual", end = 2001, check_metadata = TRUE), err) + expect_no_error(eia_data("electricity/retail-sales", freq = "annual", end = "2001")) + err <- "No data available - check temporal inputs." + expect_error(eia_data("electricity/retail-sales", freq = "annual", end = "1980"), err) err <- "'end' is before the start of available data." - expect_error(eia_data("electricity/retail-sales", freq = "annual", end = 1980, check_metadata = TRUE), err) - eia_clear_cache() expect_error(eia_data("electricity/retail-sales", freq = "annual", end = "1980", check_metadata = TRUE), err) - err <- "'end' must be a string of format: YYYY" + err <- "'end' must be a character string of format: YYYY" expect_error(eia_data("electricity/retail-sales", freq = "annual", end = "2020-06", check_metadata = TRUE), err) - err <- "'end' must be a string of format: YYYY-MM" - expect_error(eia_data("electricity/retail-sales", freq = "monthly", end = 2020, check_metadata = TRUE), err) - eia_clear_cache() + err <- "'end' must be a character string of format: YYYY-MM" expect_error(eia_data("electricity/retail-sales", freq = "monthly", end = "2020", check_metadata = TRUE), err) wrn <- "'end' is beyond available history. Latest available: 2023-08" expect_warning( eia_data( "electricity/retail-sales", "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", end = 2099, + freq = "annual", end = "2099", check_metadata = TRUE ) ) + # Test returned temporal resolutions + xa1 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "annual", "2021", "2022") + expect_equal(nrow(xa1), 1) + expect_equal(ncol(xa1), 11) + xm1 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "monthly", "2021", "2022") + expect_equal(nrow(xm1), 12) + expect_equal(ncol(xm1), 11) + xw1 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "weekly", "2021", "2022") + expect_equal(nrow(xw1), 53) + expect_equal(ncol(xw1), 11) + xd1 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "daily", "2021", "2022") + expect_equal(nrow(xd1), 256) + expect_equal(ncol(xd1), 11) + xa2 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "annual", "2021-01-01", "2022-01-01") + expect_equal(nrow(xa2), 1) + expect_equal(ncol(xa2), 11) + expect_equal(dim(xa1), dim(xa2)) + xm2 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "monthly", "2021-01-01", "2022-01-01") + expect_equal(nrow(xm2), 12) + expect_equal(ncol(xm2), 11) + expect_equal(dim(xm1), dim(xm2)) + xw2 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "weekly", "2021-01-01", "2022-01-01") + expect_equal(nrow(xw2), 53) + expect_equal(ncol(xw2), 11) + expect_equal(dim(xw1), dim(xw2)) + xd2 <- eia_data("petroleum/pri/spt", "value", list(series = "RBRTE"), "daily", "2021-01-01", "2022-01-01") + expect_equal(nrow(xd2), 256) + expect_equal(ncol(xd2), 11) + expect_equal(dim(xd2), dim(xd2)) + }) @@ -151,7 +179,7 @@ test_that("'sort' error/warning messages return as expected", { eia_data( "electricity/retail-sales", "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", start = 2020, end = 2020, + freq = "annual", start = "2020", end = "2020", sort = list(cols = c("period", "stateid"), order = "asc") ) ) @@ -159,7 +187,7 @@ test_that("'sort' error/warning messages return as expected", { eia_data( "electricity/retail-sales", "price", facets = list(stateid = "OH", sectorid = "RES"), - freq = "annual", start = 2020, end = 2020, + freq = "annual", start = "2020", end = "2020", sort = list(cols = "period", order = c("asc", "desc"))) ) err <- paste0( @@ -198,22 +226,16 @@ test_that("'length' and 'offset' error/warning messages return as expected", { expect_warning(x <- eia_data("electricity/retail-sales", length = 10)) expect_equal(ncol(x), 5) expect_equal(nrow(x), 10) - expect_warning(x <- eia_data("electricity/retail-sales", length = "10")) - expect_equal(ncol(x), 5) - expect_equal(nrow(x), 10) - err <- "'length' must be a single value between 0 and 5000." + err <- "'length' must be a numeric value between 0 and 5000." + expect_error(eia_data("electricity/retail-sales", length = "5000")) expect_error(eia_data("electricity/retail-sales", length = 5001)) - expect_error(eia_data("electricity/retail-sales", length = "5001")) # Test "offset" input value expect_warning(x <- eia_data("electricity/retail-sales", length = 10, offset = 10)) expect_equal(ncol(x), 5) expect_equal(nrow(x), 10) - expect_warning(x <- eia_data("electricity/retail-sales", length = 10, offset = "10")) - expect_equal(ncol(x), 5) - expect_equal(nrow(x), 10) - err <- "'offset' must be a single value greater than 0." - expect_error(eia_data("electricity/retail-sales", offset = -1), err) + err <- "'offset' must be a numeric value greater than 0." expect_error(eia_data("electricity/retail-sales", offset = "-1"), err) + expect_error(eia_data("electricity/retail-sales", offset = -1), err) })