From 4601e549e42400b9d3b2e1a161d86d97ca888837 Mon Sep 17 00:00:00 2001 From: rafnuss Date: Fri, 28 Oct 2022 15:18:50 -0400 Subject: [PATCH 01/11] Add `ebirdchecklist()` --- NAMESPACE | 1 + R/ebirdchecklist.R | 35 +++++++++++++++++++++++++++++++++++ man/ebirdchecklist.Rd | 31 +++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 R/ebirdchecklist.R create mode 100644 man/ebirdchecklist.Rd diff --git a/NAMESPACE b/NAMESPACE index b877396..e6f17c7 100755 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(ebirdchecklist) export(ebirdchecklistfeed) export(ebirdfreq) export(ebirdgeo) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R new file mode 100644 index 0000000..60e1b28 --- /dev/null +++ b/R/ebirdchecklist.R @@ -0,0 +1,35 @@ +#' View Checklist +#' +#' @param subId The checklist identifier +#' @param key eBird API key. You can obtain one from https://ebird.org/api/keygen. +#' We strongly recommend storing it in your \code{.Renviron} file as an +#' environment variable called \code{EBIRD_KEY}. +#' @param ... Curl options passed on to \code{\link[httr]{GET}} +#' +#' @return A data.frame containing: +#' @export +#' +#' +#' @examples \dontrun{ +#' ebirdchecklist("S121423354") +#' } +#' @references \url{http://ebird.org/} +ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { + + url <- paste0(ebase(), "product/checklist/view/", subId) + + args <- list() + + Sys.sleep(sleep) + + tt <- GET(URLencode(url), + query = ebird_compact(args), + add_headers("X-eBirdApiToken" = get_key(key)), # removed config = add_headers(... + # to allow config = to be specified in ... + ...) + + content(tt, as = "text", encoding = "UTF-8") %>% + fromJSON(FALSE) %>% + purrr::keep(!names(.) %in% c("subAux","subAuxAi")) %>% + as_tibble() +} diff --git a/man/ebirdchecklist.Rd b/man/ebirdchecklist.Rd new file mode 100644 index 0000000..f2e216b --- /dev/null +++ b/man/ebirdchecklist.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ebirdchecklist.R +\name{ebirdchecklist} +\alias{ebirdchecklist} +\title{View Checklist} +\usage{ +ebirdchecklist(subId, sleep = 0, key = NULL, ...) +} +\arguments{ +\item{subId}{The checklist identifier} + +\item{key}{eBird API key. You can obtain one from https://ebird.org/api/keygen. +We strongly recommend storing it in your \code{.Renviron} file as an +environment variable called \code{EBIRD_KEY}.} + +\item{...}{Curl options passed on to \code{\link[httr]{GET}}} +} +\value{ +A data.frame containing: +} +\description{ +View Checklist +} +\examples{ +\dontrun{ +ebirdchecklist("S121423354") +} +} +\references{ +\url{http://ebird.org/} +} From 4d158d52683a595e873ff2aa4fdc6e8f5c287577 Mon Sep 17 00:00:00 2001 From: rafnuss Date: Fri, 28 Oct 2022 15:59:12 -0400 Subject: [PATCH 02/11] correction --- R/ebirdchecklist.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index 60e1b28..4ce4197 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -29,7 +29,6 @@ ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { ...) content(tt, as = "text", encoding = "UTF-8") %>% - fromJSON(FALSE) %>% - purrr::keep(!names(.) %in% c("subAux","subAuxAi")) %>% - as_tibble() + fromJSON() %>% + bind_rows() } From 91620a832506425a362f54ffd5c2993ca04e284a Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 2 Feb 2024 10:48:55 -0500 Subject: [PATCH 03/11] Adding tests and docs --- R/ebirdchecklist.R | 8 ++++--- tests/testthat/test-ebirdchecklist.R | 31 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 tests/testthat/test-ebirdchecklist.R diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index 4ce4197..d9fefc4 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -5,11 +5,13 @@ #' We strongly recommend storing it in your \code{.Renviron} file as an #' environment variable called \code{EBIRD_KEY}. #' @param ... Curl options passed on to \code{\link[httr]{GET}} +#' @param sleep Time (in seconds) before function sends API call (defaults to +#' zero. Set to higher number if you are using this function in a loop with +#' many API calls). #' #' @return A data.frame containing: #' @export #' -#' #' @examples \dontrun{ #' ebirdchecklist("S121423354") #' } @@ -28,7 +30,7 @@ ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { # to allow config = to be specified in ... ...) - content(tt, as = "text", encoding = "UTF-8") %>% - fromJSON() %>% + content(tt, as = "text", encoding = "UTF-8") |> + fromJSON() |> bind_rows() } diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R new file mode 100644 index 0000000..bee7481 --- /dev/null +++ b/tests/testthat/test-ebirdchecklist.R @@ -0,0 +1,31 @@ +context("ebirdchecklist") + +test_that("ebirdchecklist succeeds reproducibly", { + skip_on_cran() + skip_on_ci() + + # You will need a valid eBird API key for testing + valid_checklist_id <- "S160276772" + + out1 <- ebirdchecklist(valid_checklist_id) + + + expect_is(out1, "data.frame") + expect_true(nrow(out1) > 0) + expect_true(ncol(out1) > 0) + expect_true("checklistId" %in% names(out1), + info = "checklistId column not found in the output") + + # Check if the first row's checklistId is "CL28273" + expect_equal(out1$checklistId[1], "CL28273", + info = "First row's checklistId is not CL28273") +}) + +test_that("ebirdchecklist errors for bad input", { + skip_on_cran() + skip_on_ci() + + invalid_checklist_id <- "invalid_id" + + expect_error(ebirdchecklist(invalid_checklist_id)) +}) From cdae2052ad7cc0028d4c0ebbad83e1ef814d0775 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 2 Feb 2024 10:57:27 -0500 Subject: [PATCH 04/11] Change order of param documentation --- R/ebirdchecklist.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index d9fefc4..bef5518 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -1,13 +1,13 @@ #' View Checklist #' #' @param subId The checklist identifier +#' @param sleep Time (in seconds) before function sends API call (defaults to +#' zero. Set to higher number if you are using this function in a loop with +#' many API calls). #' @param key eBird API key. You can obtain one from https://ebird.org/api/keygen. #' We strongly recommend storing it in your \code{.Renviron} file as an #' environment variable called \code{EBIRD_KEY}. #' @param ... Curl options passed on to \code{\link[httr]{GET}} -#' @param sleep Time (in seconds) before function sends API call (defaults to -#' zero. Set to higher number if you are using this function in a loop with -#' many API calls). #' #' @return A data.frame containing: #' @export From 3f5b6d5f3eb765904eb186d4a414172650cb1697 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Fri, 2 Feb 2024 11:22:10 -0500 Subject: [PATCH 05/11] Ran devtools::document() --- DESCRIPTION | 2 +- man/ebirdchecklist.Rd | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3c6bbb9..8de2a25 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -33,7 +33,7 @@ Suggests: rmarkdown, testthat, covr -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 X-schema.org-applicationCategory: Data Access X-schema.org-keywords: birds, birding, ebird, database, data, biology, observations, sightings, ornithology X-schema.org-isPartOf: https://ropensci.org diff --git a/man/ebirdchecklist.Rd b/man/ebirdchecklist.Rd index f2e216b..2ce33b0 100644 --- a/man/ebirdchecklist.Rd +++ b/man/ebirdchecklist.Rd @@ -9,6 +9,10 @@ ebirdchecklist(subId, sleep = 0, key = NULL, ...) \arguments{ \item{subId}{The checklist identifier} +\item{sleep}{Time (in seconds) before function sends API call (defaults to +zero. Set to higher number if you are using this function in a loop with +many API calls).} + \item{key}{eBird API key. You can obtain one from https://ebird.org/api/keygen. We strongly recommend storing it in your \code{.Renviron} file as an environment variable called \code{EBIRD_KEY}.} From aa95f4d7b4c5a729836b494612e874e0edd8311f Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Sun, 17 Mar 2024 10:50:47 -0400 Subject: [PATCH 06/11] fix: Make ebirdchecklist throw an error if ID is invalid --- R/ebirdchecklist.R | 24 +++++++++++++++--------- tests/testthat/test-ebirdchecklist.R | 4 +++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index bef5518..9c894be 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -20,17 +20,23 @@ ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { url <- paste0(ebase(), "product/checklist/view/", subId) - args <- list() - Sys.sleep(sleep) - tt <- GET(URLencode(url), - query = ebird_compact(args), - add_headers("X-eBirdApiToken" = get_key(key)), # removed config = add_headers(... - # to allow config = to be specified in ... + response <- GET(URLencode(url), + query = ebird_compact(list()), + add_headers("X-eBirdApiToken" = get_key(key)), ...) - content(tt, as = "text", encoding = "UTF-8") |> - fromJSON() |> - bind_rows() + content_text <- content(response, as = "text", encoding = "UTF-8") + content_json <- fromJSON(content_text, flatten = FALSE) + + # Check if the response contains an error message + if ("error" %in% names(content_json) || "errors" %in% names(content_json)) { + error_message <- ifelse("error" %in% names(content_json), + paste("Error detected:", content_json$error$status, content_json$error$title), + paste("Errors detected:", content_json$errors$status, content_json$errors$title)) + stop("Checklist ID is invalid") + } + + bind_rows(content_json) } diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R index bee7481..1d73452 100644 --- a/tests/testthat/test-ebirdchecklist.R +++ b/tests/testthat/test-ebirdchecklist.R @@ -27,5 +27,7 @@ test_that("ebirdchecklist errors for bad input", { invalid_checklist_id <- "invalid_id" - expect_error(ebirdchecklist(invalid_checklist_id)) + # Expect an error and check if the error message matches the expected pattern + expect_error(ebirdchecklist(invalid_checklist_id), + "Checklist ID is invalid") }) From 05694f238af43ddcbe854bfae07b8400e315f4f9 Mon Sep 17 00:00:00 2001 From: Dave Slager Date: Fri, 22 Mar 2024 21:48:34 -0700 Subject: [PATCH 07/11] tweak error handling --- R/ebirdchecklist.R | 12 +++++++----- tests/testthat/test-ebirdchecklist.R | 3 +-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index 9c894be..bbf9f5e 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -31,11 +31,13 @@ ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { content_json <- fromJSON(content_text, flatten = FALSE) # Check if the response contains an error message - if ("error" %in% names(content_json) || "errors" %in% names(content_json)) { - error_message <- ifelse("error" %in% names(content_json), - paste("Error detected:", content_json$error$status, content_json$error$title), - paste("Errors detected:", content_json$errors$status, content_json$errors$title)) - stop("Checklist ID is invalid") + if (any(grepl('^error', names(content_json)))){ + err_msg <- 'Unknown error' + err_msg <- try(content_json$errors$status, silent = TRUE) + if (grepl('subId is invalid', content_json$errors$title)){ + err_msg <- 'subId is invalid' + } + stop(err_msg) } bind_rows(content_json) diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R index 1d73452..cc4e2b4 100644 --- a/tests/testthat/test-ebirdchecklist.R +++ b/tests/testthat/test-ebirdchecklist.R @@ -28,6 +28,5 @@ test_that("ebirdchecklist errors for bad input", { invalid_checklist_id <- "invalid_id" # Expect an error and check if the error message matches the expected pattern - expect_error(ebirdchecklist(invalid_checklist_id), - "Checklist ID is invalid") + expect_error(ebirdchecklist(invalid_checklist_id), "subId is invalid") }) From dd35115f35eab9dce0d00f5cb1c6b6ee9a958eca Mon Sep 17 00:00:00 2001 From: Dave Slager Date: Fri, 22 Mar 2024 21:50:51 -0700 Subject: [PATCH 08/11] tweak, info arg is deprecated --- tests/testthat/test-ebirdchecklist.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R index cc4e2b4..c546f34 100644 --- a/tests/testthat/test-ebirdchecklist.R +++ b/tests/testthat/test-ebirdchecklist.R @@ -13,12 +13,10 @@ test_that("ebirdchecklist succeeds reproducibly", { expect_is(out1, "data.frame") expect_true(nrow(out1) > 0) expect_true(ncol(out1) > 0) - expect_true("checklistId" %in% names(out1), - info = "checklistId column not found in the output") + expect_true("checklistId" %in% names(out1)) # Check if the first row's checklistId is "CL28273" - expect_equal(out1$checklistId[1], "CL28273", - info = "First row's checklistId is not CL28273") + expect_equal(out1$checklistId[1], "CL28273") }) test_that("ebirdchecklist errors for bad input", { From cfa5f5b617e70730b4038ba70f29edb0c40c8b30 Mon Sep 17 00:00:00 2001 From: Dave Slager Date: Fri, 22 Mar 2024 22:59:23 -0700 Subject: [PATCH 09/11] merge result and update tests --- R/ebirdchecklist.R | 60 +++++++++++++++++++++++++++- tests/testthat/test-ebirdchecklist.R | 19 ++++----- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index bbf9f5e..c57272b 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -16,7 +16,7 @@ #' ebirdchecklist("S121423354") #' } #' @references \url{http://ebird.org/} -ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { +ebirdchecklist <- function(subId, sleep = 0, key = NULL, other = FALSE, ...) { url <- paste0(ebase(), "product/checklist/view/", subId) @@ -40,5 +40,61 @@ ebirdchecklist <- function(subId, sleep = 0, key = NULL, ...) { stop(err_msg) } - bind_rows(content_json) + cl <- bind_rows(content_json) + + # extract sub df + col_is_df <- vapply(cl, is.data.frame, TRUE) + sub_df <- cl[1, !col_is_df] + # 'comments' column has name duplicated with species comments + names(sub_df)[names(sub_df) == 'comments'] <- 'subComments' + + # extract subAux df + subAux_df <- cl$subAux[1,] + # seems empty, and names conflict with breeding codes + subAux_df$auxCode <- NULL + subAux_df$entryMethodCode <- NULL + + # extract obsAux df + obsAux_list <- cl$obs$obsAux + # find the list entry that contains the data + col_is_df <- vapply(obsAux_list, is.data.frame, TRUE) + obsAux_df <- obsAux_list[[which(col_is_df)]] + # redundant columns from sub_df + obsAux_df$subId <- NULL + obsAux_df$speciesCode <- NULL + # duplicate info with uninformative name + obsAux_df$value <- NULL + # names conflict with sub_df, and not very important + obsAux_df$fieldName <- NULL + obsAux_df$entryMethodCode <- NULL + + # extract obs df + obs_df <- cl$obs + obs_df$obsAux <- NULL + # hideFlags might be useful, but its structure is currently undocumented + obs_df$hideFlags <- NULL + # remove redundant sub-level columns already in sub_df + obs_df$subnational1Code <- NULL + obs_df$obsDt <- NULL + obs_df$projId <- NULL + # mediaCounts appears to just be a nested integer vector (?) + obs_df$mediaCounts <- Reduce(c, obs_df$mediaCounts) + # 'comments' column has name duplicated with checklist comments + names(obs_df)[names(obs_df) == 'comments'] <- 'obsComments' + + # join to get result df + out_df <- sub_df + if (! is.null(subAux_df) && other){ + out_df <- dplyr::left_join(out_df, subAux_df, by = 'subId') + } + out_df <- dplyr::left_join(out_df, obs_df, by = 'subId') + if (! is.null(obsAux_df)){ + out_df <- dplyr::left_join(out_df, obsAux_df, by = 'obsId') + } + # remove some unneeded columns by default + if (! other){ + regex <- '^projId$|^howManyAt|^hideFlags$|^present$|^submissionMethod' + out_df <- out_df[, !grepl(regex, names(out_df)), drop = FALSE] + } + out_df } diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R index c546f34..68f401a 100644 --- a/tests/testthat/test-ebirdchecklist.R +++ b/tests/testthat/test-ebirdchecklist.R @@ -1,27 +1,22 @@ -context("ebirdchecklist") - test_that("ebirdchecklist succeeds reproducibly", { - skip_on_cran() - skip_on_ci() - # You will need a valid eBird API key for testing - valid_checklist_id <- "S160276772" + expect_no_error(out1 <- ebirdchecklist("S117450946")) - out1 <- ebirdchecklist(valid_checklist_id) + # check all list-columns removed during preprocessing + expect_false(any(vapply(out1, is.list, logical(1)))) + # Works with breeding code + expect_true('ON' %in% out1$auxCode) expect_is(out1, "data.frame") - expect_true(nrow(out1) > 0) + expect_true(nrow(out1) == 6) expect_true(ncol(out1) > 0) expect_true("checklistId" %in% names(out1)) + expect_equal(out1$checklistId[1], "CL24321") - # Check if the first row's checklistId is "CL28273" - expect_equal(out1$checklistId[1], "CL28273") }) test_that("ebirdchecklist errors for bad input", { - skip_on_cran() - skip_on_ci() invalid_checklist_id <- "invalid_id" From 1384d131c38dec03d24317b4117d237d1f3302ab Mon Sep 17 00:00:00 2001 From: Dave Slager Date: Fri, 22 Mar 2024 23:03:59 -0700 Subject: [PATCH 10/11] vcr tests for ebirdchecklist --- tests/fixtures/ebirdchecklist.yml | 81 ++++++++++++++++++++++++++++ tests/testthat/test-ebirdchecklist.R | 34 ++++++------ 2 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 tests/fixtures/ebirdchecklist.yml diff --git a/tests/fixtures/ebirdchecklist.yml b/tests/fixtures/ebirdchecklist.yml new file mode 100644 index 0000000..fddef93 --- /dev/null +++ b/tests/fixtures/ebirdchecklist.yml @@ -0,0 +1,81 @@ +http_interactions: +- request: + method: get + uri: https://ebird.org/ws2.0/product/checklist/view/S117450946 + body: + encoding: '' + string: '' + headers: + Accept: application/json, text/xml, application/xml, */* + X-eBirdApiToken: <<>> + response: + status: + status_code: 200 + category: Success + reason: OK + message: 'Success: (200) OK' + headers: + cache-control: no-cache, no-store, max-age=0, must-revalidate + content-encoding: gzip + content-type: application/json;charset=utf-8 + date: Sat, 23 Mar 2024 05:57:03 GMT + expires: '0' + pragma: no-cache + server: Apache + strict-transport-security: max-age=31536000 ; includeSubDomains + vary: Origin,Accept-Encoding,Access-Control-Request-Method,Access-Control-Request-Headers + x-content-type-options: nosniff + x-frame-options: DENY + x-xss-protection: 1; mode=block + content-length: '572' + body: + encoding: '' + file: no + string: '{"projId":"EBIRD","subId":"S117450946","protocolId":"P21","locId":"L2906552","durationHrs":0.05,"allObsReported":true,"comments":"7 + passing cars","creationDt":"2022-08-23 15:18","lastEditedDt":"2022-08-23 15:18","obsDt":"2022-05-30 + 06:55","obsTimeValid":true,"checklistId":"CL24321","numObservers":1,"subnational1Code":"US-WA","submissionMethodCode":"EBIRD_upload","userDisplayName":"Dave + Slager","numSpecies":5,"subAux":[{"subId":"S117450946","fieldName":"nocturnal","entryMethodCode":"ebird_nocturnal","auxCode":"0"}],"subAuxAi":[],"obs":[{"speciesCode":"hummin","hideFlags":[],"obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":1,"howManyAtmost":1,"present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894279","howManyStr":"1"},{"speciesCode":"eursta","hideFlags":[],"exoticCategory":"N","obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":6,"howManyAtmost":6,"present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894277","howManyStr":"6"},{"speciesCode":"amerob","hideFlags":[],"obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":2,"howManyAtmost":2,"present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894275","howManyStr":"2"},{"speciesCode":"cedwax","hideFlags":[],"obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":2,"howManyAtmost":2,"present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894278","howManyStr":"2"},{"speciesCode":"houspa","hideFlags":[],"exoticCategory":"N","obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":1,"howManyAtmost":1,"comments":"ON","present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894274","howManyStr":"1","obsAux":[{"subId":"S117450946","fieldName":"breeding_code","entryMethodCode":"ebird_breeding_code","auxCode":"ON","obsId":"OBS1503894274","speciesCode":"houspa","value":"ON"}]},{"speciesCode":"pswspa1","hideFlags":[],"obsDt":"2022-05-30 + 06:55","subnational1Code":"US-WA","howManyAtleast":1,"howManyAtmost":1,"present":false,"subId":"S117450946","projId":"EBIRD","obsId":"OBS1503894276","howManyStr":"1"}]}' + recorded_at: 2024-03-23 06:01:43 GMT + recorded_with: vcr/1.2.2, webmockr/0.9.0 +- request: + method: get + uri: https://ebird.org/ws2.0/product/checklist/view/invalid_id + body: + encoding: '' + string: '' + headers: + Accept: application/json, text/xml, application/xml, */* + X-eBirdApiToken: <<>> + response: + status: + status_code: 400 + category: Client error + reason: Bad Request + message: 'Client error: (400) Bad Request' + headers: + cache-control: no-cache, no-store, max-age=0, must-revalidate + content-encoding: gzip + content-type: application/json + date: Sat, 23 Mar 2024 05:57:04 GMT + expires: '0' + pragma: no-cache + server: Apache + strict-transport-security: max-age=31536000 ; includeSubDomains + vary: Origin,Accept-Encoding,Access-Control-Request-Method,Access-Control-Request-Headers + x-content-type-options: nosniff + x-frame-options: DENY + x-xss-protection: 1; mode=block + content-length: '129' + body: + encoding: '' + file: no + string: '{"errors":[{"status":"400 BAD_REQUEST","code":"Pattern","title":"Field + subId of checklistBySubIdCmd: subId is invalid."}]}' + recorded_at: 2024-03-23 06:01:43 GMT + recorded_with: vcr/1.2.2, webmockr/0.9.0 diff --git a/tests/testthat/test-ebirdchecklist.R b/tests/testthat/test-ebirdchecklist.R index 68f401a..2c3034d 100644 --- a/tests/testthat/test-ebirdchecklist.R +++ b/tests/testthat/test-ebirdchecklist.R @@ -1,25 +1,27 @@ -test_that("ebirdchecklist succeeds reproducibly", { +vcr::use_cassette("ebirdchecklist", { + test_that("ebirdchecklist succeeds reproducibly", { - expect_no_error(out1 <- ebirdchecklist("S117450946")) + expect_no_error(out1 <- ebirdchecklist("S117450946")) - # check all list-columns removed during preprocessing - expect_false(any(vapply(out1, is.list, logical(1)))) + # check all list-columns removed during preprocessing + expect_false(any(vapply(out1, is.list, logical(1)))) - # Works with breeding code - expect_true('ON' %in% out1$auxCode) + # Works with breeding code + expect_true('ON' %in% out1$auxCode) - expect_is(out1, "data.frame") - expect_true(nrow(out1) == 6) - expect_true(ncol(out1) > 0) - expect_true("checklistId" %in% names(out1)) - expect_equal(out1$checklistId[1], "CL24321") + expect_is(out1, "data.frame") + expect_true(nrow(out1) == 6) + expect_true(ncol(out1) > 0) + expect_true("checklistId" %in% names(out1)) + expect_equal(out1$checklistId[1], "CL24321") -}) + }) -test_that("ebirdchecklist errors for bad input", { + test_that("ebirdchecklist errors for bad input", { - invalid_checklist_id <- "invalid_id" + invalid_checklist_id <- "invalid_id" - # Expect an error and check if the error message matches the expected pattern - expect_error(ebirdchecklist(invalid_checklist_id), "subId is invalid") + # Expect an error and check if the error message matches the expected pattern + expect_error(ebirdchecklist(invalid_checklist_id), "subId is invalid") + }) }) From e9ed9929b5e729a105503bb0a587b5197ea9bef1 Mon Sep 17 00:00:00 2001 From: Dave Slager Date: Fri, 22 Mar 2024 23:22:13 -0700 Subject: [PATCH 11/11] document ebirdchecklist --- R/ebirdchecklist.R | 38 ++++++++++++++++++++++++----- man/ebirdchecklist.Rd | 57 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/R/ebirdchecklist.R b/R/ebirdchecklist.R index c57272b..9e0948e 100644 --- a/R/ebirdchecklist.R +++ b/R/ebirdchecklist.R @@ -2,14 +2,40 @@ #' #' @param subId The checklist identifier #' @param sleep Time (in seconds) before function sends API call (defaults to -#' zero. Set to higher number if you are using this function in a loop with -#' many API calls). -#' @param key eBird API key. You can obtain one from https://ebird.org/api/keygen. -#' We strongly recommend storing it in your \code{.Renviron} file as an -#' environment variable called \code{EBIRD_KEY}. +#' zero. Set to higher number if you are using this function in a loop with +#' many API calls). +#' @param key eBird API key. You can obtain one from +#' https://ebird.org/api/keygen. We strongly recommend storing it in your +#' \code{.Renviron} file as an environment variable called \code{EBIRD_KEY}. +#' @param other FALSE (default) or TRUE. Whether to return some +#' optional/deprecated/unsupported columns. Currently these are all columns in +#' subAux, projId, howManyAt*, hideFlags, present, and submissionMethod*. #' @param ... Curl options passed on to \code{\link[httr]{GET}} #' -#' @return A data.frame containing: +#' @return A 'tibble' 'data.frame' containing checklist information: +#' @return "subId": submission ID +#' @return "protocolId": eBird protocol ID +#' @return "locId": location ID +#' @return "durationHrs": checklist duration, in hours +#' @return "allObsReported": whether all observations were reported, i.e., +#' whether it was a 'complete' checklist +#' @return "subComments": checklist comments +#' @return "creationDt": checklist creation date +#' @return "lastEditedDt": checklist last edited date +#' @return "obsDt": checklist date-time +#' @return "obsTimeValid": whether checklist date-time is valid +#' @return "checklistId" checklist ID +#' @return "numObservers" number of observers on checklist +#' @return "subnational1Code" country code and subnational1 code +#' @return "userDisplayName" eBird user display name +#' @return "numSpecies" number of species reported on checklist +#' @return "speciesCode" species codes reported on checklist +#' @return "obsId" observation IDs for each taxon on checklist +#' @return "howManyStr" number of individuals reported for each taxon +#' @return "exoticCategory" exotic species categories for each taxon +#' @return "obsComments" observation comments for each taxon +#' @return "auxCode" breding code for each taxon + #' @export #' #' @examples \dontrun{ diff --git a/man/ebirdchecklist.Rd b/man/ebirdchecklist.Rd index 2ce33b0..105ffbe 100644 --- a/man/ebirdchecklist.Rd +++ b/man/ebirdchecklist.Rd @@ -4,7 +4,7 @@ \alias{ebirdchecklist} \title{View Checklist} \usage{ -ebirdchecklist(subId, sleep = 0, key = NULL, ...) +ebirdchecklist(subId, sleep = 0, key = NULL, other = FALSE, ...) } \arguments{ \item{subId}{The checklist identifier} @@ -13,14 +13,61 @@ ebirdchecklist(subId, sleep = 0, key = NULL, ...) zero. Set to higher number if you are using this function in a loop with many API calls).} -\item{key}{eBird API key. You can obtain one from https://ebird.org/api/keygen. -We strongly recommend storing it in your \code{.Renviron} file as an -environment variable called \code{EBIRD_KEY}.} +\item{key}{eBird API key. You can obtain one from +https://ebird.org/api/keygen. We strongly recommend storing it in your +\code{.Renviron} file as an environment variable called \code{EBIRD_KEY}.} + +\item{other}{FALSE (default) or TRUE. Whether to return some +optional/deprecated/unsupported columns. Currently these are all columns in +subAux, projId, howManyAt*, hideFlags, present, and submissionMethod*.} \item{...}{Curl options passed on to \code{\link[httr]{GET}}} } \value{ -A data.frame containing: +A 'tibble' 'data.frame' containing checklist information: + +"subId": submission ID + +"protocolId": eBird protocol ID + +"locId": location ID + +"durationHrs": checklist duration, in hours + +"allObsReported": whether all observations were reported, i.e., + whether it was a 'complete' checklist + +"subComments": checklist comments + +"creationDt": checklist creation date + +"lastEditedDt": checklist last edited date + +"obsDt": checklist date-time + +"obsTimeValid": whether checklist date-time is valid + +"checklistId" checklist ID + +"numObservers" number of observers on checklist + +"subnational1Code" country code and subnational1 code + +"userDisplayName" eBird user display name + +"numSpecies" number of species reported on checklist + +"speciesCode" species codes reported on checklist + +"obsId" observation IDs for each taxon on checklist + +"howManyStr" number of individuals reported for each taxon + +"exoticCategory" exotic species categories for each taxon + +"obsComments" observation comments for each taxon + +"auxCode" breding code for each taxon } \description{ View Checklist