From ee9d721d2e3e747f9e1984c54b9d2633d8a0f801 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Tue, 7 Jan 2025 13:32:26 +1100 Subject: [PATCH 01/27] Increase lists query max limit #252 * Was 5000 due to previous API limit --- R/collapse_metadata.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/collapse_metadata.R b/R/collapse_metadata.R index 08fa3a7c..adca405a 100644 --- a/R/collapse_metadata.R +++ b/R/collapse_metadata.R @@ -166,7 +166,7 @@ collapse_licences <- function(){ collapse_lists <- function(.query){ url <- url_lookup("metadata/lists") |> url_parse() - url$query <- list(max = 5000) + url$query <- list(max = 10000) if(!missing(.query)){ if(!is.null(.query$slice)){ url$query <- list(max = .query$slice$slice_n) From 55b91b1af23b10b1e4944b0f7676d977efdf3e84 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Tue, 7 Jan 2025 13:40:23 +1100 Subject: [PATCH 02/27] Delete .travis.yml --- .travis.yml | 64 ----------------------------------------------------- 1 file changed, 64 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 75d15466..00000000 --- a/.travis.yml +++ /dev/null @@ -1,64 +0,0 @@ -language: r -sudo: false -cache: packages -addons: - apt: - update: true - packages: - - libv8-3.14-dev - - libmagick++-dev - - libudunits2-dev - - libgdal-dev - - libnetcdf-dev - - libharfbuzz-dev - - libfribidi-dev - - gcc-8 - - g++-8 - - gfortran-8 - -matrix: - include: - - os: linux - r: 4.1.0 - dist: bionic - env: - - BUILD_NAME=release - - DETAILS="release build" - - R_CODECOV=true -env: - global: - - MAKEFLAGS="-j 2" - - COMPILER=g++-8 - - CC=gcc-8 - - CXX=g++-8 - - NOT_CRAN=true -before_install: - - mkdir -p ~/.R && touch ~/.R/Makevars - - VER=-8 - - echo "CC=gcc$(VER)" >> ~/.R/Makevars - - echo "CXX=g++$(VER)" >> ~/.R/Makevars - - echo "CXX1X=g++$(VER)" >> ~/.R/Makevars - - echo "FC=gfortran$(VER)" >> ~/.R/Makevars - - echo "F77=gfortran$(VER)" >> ~/.R/Makevars - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-8 - - sudo update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-8 60 -warnings_are_errors: true -script: -- | - R CMD build . - travis_wait 20 R CMD check galah*tar.gz -after_success: -- if [[ "${R_CODECOV}" ]]; then travis_wait 20 Rscript -e 'covr::codecov()'; fi -- test $TRAVIS_BRANCH = "master" && - Rscript -e 'pkgdown::build_site()' -notifications: - slack: - secure: "PGhowadCkFSLSmiBLbR5CZ6Bdkiaijz3PJ9GtBa6kLGtLtKIMkvK+9LrDkIdJuElNUNBz6iz4wqBlTaLOo83HEHt6Sf8nL1WcJfGQCaQMo0Xn/ivU0slwdjA08u9Trcr9UySNJGFbbwu3vzEevd6DityFRibEP4vm8t38tNttKM=" -deploy: - skip_cleanup: true - provider: pages - github-token: $GITHUB_PAT - keep-history: true - local-dir: docs - on: - branches: [main, master] From f2d669be63c884af59a89bd2bf0925a564568a52 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Wed, 8 Jan 2025 15:53:48 +1100 Subject: [PATCH 03/27] Fix for #254 Bug resulted from parsing error in code designed to detect number of facets. Have added two tests for this. --- R/parse_occurrences_count.R | 6 +++++- tests/testthat/test-galah_group_by.R | 12 ++++++++++++ tests/testthat/test-international-Guatemala.R | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/R/parse_occurrences_count.R b/R/parse_occurrences_count.R index dab08cbe..c69fb3ff 100644 --- a/R/parse_occurrences_count.R +++ b/R/parse_occurrences_count.R @@ -225,7 +225,11 @@ check_facet_count <- function(.query, warn = TRUE, error_call = caller_env()){ temp_data$url <- url_build(url) temp_data$slot_name <- NULL result <- query_API(temp_data) - lapply(result, function(a){a$count}) |> unlist() + if(length(result) < 1){ + 0 + }else{ + lapply(result, function(a){a$count}) |> unlist() + } # if(inherits(result, "data.frame")){ # group_by arg present # n_available <- result$count # if(length(n_available) > 1){ # is this correct? What about multiple fields? diff --git a/tests/testthat/test-galah_group_by.R b/tests/testthat/test-galah_group_by.R index 30b561c9..766f0c50 100644 --- a/tests/testthat/test-galah_group_by.R +++ b/tests/testthat/test-galah_group_by.R @@ -51,6 +51,18 @@ test_that("atlas_counts returns all counts if no limit is provided", { expect_gte(nrow(counts), 5) }) +test_that("atlas_counts returns an empty tibble if number of records = 0", { + skip_if_offline() + counts <- galah_call() |> + filter(year < 1900 & year > 2000) |> + count() |> + group_by(species) |> + collect() + expect_s3_class(counts, c("tbl_df", "tbl", "data.frame")) + expect_equal(nrow(counts), 0) + expect_equal(ncol(counts), 0) +}) + test_that("grouped atlas_counts for species returns expected output", { skip_if_offline() counts <- galah_call() |> diff --git a/tests/testthat/test-international-Guatemala.R b/tests/testthat/test-international-Guatemala.R index 1b3b970b..ed31c818 100644 --- a/tests/testthat/test-international-Guatemala.R +++ b/tests/testthat/test-international-Guatemala.R @@ -146,6 +146,20 @@ test_that("atlas_counts works with group_by for Guatemala", { expect_equal(names(result), c("basis_of_record", "count")) }) +test_that("atlas_counts works with group_by for Guatemala when count = 0", { + skip_if_offline() + result <- galah_call() |> + identify("Mammalia") |> + filter(!is.na(all_image_url)) |> + group_by("family") |> + count() |> + collect() |> + try(silent = TRUE) + skip_if(inherits(result, "try-error"), message = "API not available") + expect_equal(nrow(result), 0) + expect_equal(ncol(result), 0) +}) + test_that("atlas_species fails for Guatemala due to unavailable API", { skip_if_offline() galah_config( From ff5fb90e61cd40ca4d2863ed312e7e054ef78128 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Tue, 14 Jan 2025 15:03:17 +1100 Subject: [PATCH 04/27] Fix bracket parsing for multi-filter queries #251 * Negative statement queries no longer add extra set of brackets (which breaks them in other contexts) * Multiple OR + AND statements no longer break due to parsing of incorrect number of brackets --- R/build.R | 13 +++++++++++-- R/quosure_handling.R | 8 ++++++-- tests/testthat/test-galah_filter.R | 14 +++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/R/build.R b/R/build.R index 2bb57468..d819390c 100644 --- a/R/build.R +++ b/R/build.R @@ -128,11 +128,20 @@ build_single_fq <- function(query){ # ensure all arguments from galah_filter are enclosed in brackets # EXCEPT for assertions fq <- query$fq - missing_brackets <- !grepl("^\\(", fq) & !grepl("assertions", fq) + missing_brackets <- + !grepl("^\\(", fq) & # already has brackets + !grepl("assertions", fq) & # assertions don't need additional brackets + !grepl("^-\\(", fq) # negative query already has brackets if(any(missing_brackets)){ fq[missing_brackets] <- paste0("(", fq[missing_brackets], ")") } - fq_single <- paste(fq, collapse = "AND") + # add brackets to non-negative AND statements + # (adding additional brackets to negative statements breaks them) + if(any(!grepl("^-\\(", fq))) { + fq_single <- glue::glue_collapse(glue("{fq}"), "AND") + } else { + fq_single <- glue::glue_collapse(glue("({fq})"), "AND") + } c(fq = fq_single, query[names(query) != "fq"]) }else{ query diff --git a/R/quosure_handling.R b/R/quosure_handling.R index 8fa1c2cd..9fb21ef8 100644 --- a/R/quosure_handling.R +++ b/R/quosure_handling.R @@ -106,8 +106,12 @@ concatenate_assertions <- function(df, logical){ #' @noRd #' @keywords Internal clean_logical_statements <- function(df){ - or_lookup <- grepl("\\sOR\\s", df$query) # add AND here? - if(any(or_lookup)){ + or_lookup <- + grepl("\\sOR\\s", df$query) & # OR statements + !grepl("^-\\(", df$query) # but not negative statements + # browser() + if(any(or_lookup) && + length(df$query) > 1){ # multiple OR + AND queries require ORs to have extra brackets or_strings <- df$query[which(or_lookup)] df$query[which(or_lookup)] <- glue("({or_strings})") |> as.character() diff --git a/tests/testthat/test-galah_filter.R b/tests/testthat/test-galah_filter.R index 4f2a9f5b..4281364d 100644 --- a/tests/testthat/test-galah_filter.R +++ b/tests/testthat/test-galah_filter.R @@ -134,7 +134,7 @@ test_that("galah_filter handles OR statements", { filters <- galah_filter(year == 2010 | year == 2020) expect_equal(nrow(filters), 1) expect_equal(filters$value, "2010|2020") - expect_equal(filters$query, "((year:\"2010\") OR (year:\"2020\"))") + expect_equal(filters$query, "(year:\"2010\") OR (year:\"2020\")") }) test_that("galah_filter handles OR statements", { @@ -142,7 +142,7 @@ test_that("galah_filter handles OR statements", { raw_scientificName == "Litoria peronii") expect_equal(nrow(filters), 1) expect_equal(filters$query, - "((raw_scientificName:\"Litoria jervisiensis\") OR (raw_scientificName:\"Litoria peronii\"))") + "(raw_scientificName:\"Litoria jervisiensis\") OR (raw_scientificName:\"Litoria peronii\")") }) test_that("galah_filter works with 3 OR statements", { @@ -152,7 +152,7 @@ test_that("galah_filter works with 3 OR statements", { expect_equal(nrow(filters), 1) expect_equal(filters$value, "HumanObservation|MachineObservation|PreservedSpecimen") expect_equal(filters$query, - "((basisOfRecord:\"HumanObservation\") OR (basisOfRecord:\"MachineObservation\") OR (basisOfRecord:\"PreservedSpecimen\"))") + "(basisOfRecord:\"HumanObservation\") OR (basisOfRecord:\"MachineObservation\") OR (basisOfRecord:\"PreservedSpecimen\")") }) test_that("galah_filter handles exclusion", { @@ -266,7 +266,7 @@ test_that("galah_filter handles lsid as an input", { test_that("galah_filter handles different fields separated by OR", { filters <- galah_filter(phylum == "Chordata" | kingdom == "Plantae") - expect_equal(filters$query, "((phylum:\"Chordata\") OR (kingdom:\"Plantae\"))") + expect_equal(filters$query, "(phylum:\"Chordata\") OR (kingdom:\"Plantae\")") }) test_that("galah_filter fails when given invalid AND syntax", { @@ -301,7 +301,7 @@ test_that("galah_filter handles %in% even with multiple filters", { filter_multiple <- galah_filter(year %in% list_of_years, cl22 == "Tasmania") expect_equal(nrow(filter_single), 1) expect_equal(nrow(filter_multiple), 2) - expect_equal("((year:\"2020\") OR (year:\"2021\") OR (year:\"2022\"))", filter_single$query[[1]]) + expect_equal("(year:\"2020\") OR (year:\"2021\") OR (year:\"2022\")", filter_single$query[[1]]) expect_equal("((year:\"2020\") OR (year:\"2021\") OR (year:\"2022\"))", filter_multiple$query[[1]]) }) @@ -345,7 +345,7 @@ test_that("`galah_filter()` handles apostrophes (') correctly", { atlas_counts() expect_equal(nrow(query), 1) # returns result expect_gte(query$count[1], 1) - expect_match(filter, "\\(\\(datasetName:\\\"Australia's Virtual Herbarium\\\"\\)") + expect_match(filter, "\\(datasetName:\\\"Australia's Virtual Herbarium\\\"") }) test_that("`galah_filter()` handles multiple values with brackets correctly", { @@ -359,7 +359,7 @@ test_that("`galah_filter()` handles multiple values with brackets correctly", { atlas_counts() expect_equal(nrow(query), 1) # returns result expect_gte(query$count[1], 1) - expect_match(filter, "\\(\\(scientificName:\\\"Aviceda \\(Aviceda\\) subcristata\\\"\\)") + expect_match(filter, "\\(scientificName:\\\"Aviceda \\(Aviceda\\) subcristata\\\"\\)") }) test_that("`galah_filter()` accepts {{}} on lhs of formula", { From 9b279f943637d31175c68489de2788d8fe8e1f63 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Tue, 14 Jan 2025 17:56:33 +1100 Subject: [PATCH 05/27] Add query parsing test for `galah_filter()` * Tests that galah parses query in #251 correctly --- tests/testthat/test-galah_filter.R | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/testthat/test-galah_filter.R b/tests/testthat/test-galah_filter.R index 4281364d..59d27cd3 100644 --- a/tests/testthat/test-galah_filter.R +++ b/tests/testthat/test-galah_filter.R @@ -362,6 +362,14 @@ test_that("`galah_filter()` handles multiple values with brackets correctly", { expect_match(filter, "\\(scientificName:\\\"Aviceda \\(Aviceda\\) subcristata\\\"\\)") }) +test_that("galah_filter builds correct query with `!`, `%in%`, `c()` and `identify()`", { + ibra_subset <- c("Brigalow Belt North", "Brigalow Belt South", "Central Mackay Coast") + query <- request_data(type = "occurrences-count") |> + identify("Crinia signifera") |> + filter(!cl1048 %in% ibra_subset) + expect_equal(query$filter$query, c("-(cl1048:\"Brigalow Belt North\") OR -(cl1048:\"Brigalow Belt South\") OR -(cl1048:\"Central Mackay Coast\")")) +}) + test_that("`galah_filter()` accepts {{}} on lhs of formula", { skip_if_offline() field <- "species" From b2b17c971e0c175e1eb48c5a6aa94eb62f539c30 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Wed, 15 Jan 2025 17:12:43 +1100 Subject: [PATCH 06/27] Format column names of empty tibble to snake case #255 * Specific to `atlas_species()` results * Test added for `atlas_species()` --- R/collect_species.R | 2 +- tests/testthat/test-atlas_species.R | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/R/collect_species.R b/R/collect_species.R index 5555c3a7..e8624496 100644 --- a/R/collect_species.R +++ b/R/collect_species.R @@ -8,7 +8,7 @@ collect_species <- function(.query, file = NULL){ .query$file <- check_download_filename(file, ext = "csv") query_API(.query) result <- read_csv(.query$file, col_types = cols()) # NOTE: used to have tryCatch() - if(nrow(result) > 0){ + if(nrow(result) >= 0){ names(result) <- names(result) |> rename_columns(type = "checklist") } diff --git a/tests/testthat/test-atlas_species.R b/tests/testthat/test-atlas_species.R index 206b913b..53be0a09 100644 --- a/tests/testthat/test-atlas_species.R +++ b/tests/testthat/test-atlas_species.R @@ -92,4 +92,23 @@ test_that("collapse works when no `filter()` is supplied", { st_crop(wkt) |> collapse()}) expect_s3_class(x, "query") -}) \ No newline at end of file +}) + +test_that("atlas_species reformats column names when empty tibble is returned", { + skip_if_offline() + galah_config(run_checks = TRUE) + + # No matching species expected, an empty tibble should be returned + species <- galah_call() |> + identify("sarcopterygii") |> + filter(cl1048 == "Wet Tropics") |> + atlas_species() + expected_cols <- c("taxon_concept_id", "species_name", + "scientific_name_authorship", "taxon_rank", + "kingdom", "phylum", "class", "order", "family", + "genus", "vernacular_name") + + expect_setequal(names(species), expected_cols) + expect_equal(nrow(species), 0) + expect_s3_class(species, c("tbl_df", "tbl", "data.frame")) +}) From 366e597929a31c2872b245e0302d908bc2762b56 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Thu, 16 Jan 2025 13:44:31 +1100 Subject: [PATCH 07/27] Add Flanders to `node_metadata.csv` #256 + reorder for clarity --- data-raw/node_metadata.csv | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data-raw/node_metadata.csv b/data-raw/node_metadata.csv index 64c5ec27..a701fb9d 100644 --- a/data-raw/node_metadata.csv +++ b/data-raw/node_metadata.csv @@ -2,8 +2,8 @@ region,institution,acronym,url,supported Australia,Atlas of Living Australia,ALA,https://www.ala.org.au,TRUE Austria,Biodiversitäts-Atlas Österreich,BAO,https://biodiversityatlas.at,TRUE Brazil,Sistemas de Informações sobre a Biodiversidade Brasileira,SiBBr,https://sibbr.gov.br,TRUE -Estonia,eElurikkus,NA,https://elurikkus.ee,FALSE -France,Portail franais d'accs aux donnes d'observation sur les espces,OpenObs,https://openobs.mnhn.fr,TRUE +Flanders,Vlaams Biodiversiteitsportaal,VB,https://natuurdata.dev.inbo.be,TRUE +France,Portail français d'accès aux données d'observation sur les espèces,OpenObs,https://openobs.mnhn.fr,TRUE Global,Global Biodiversity Information Facility,GBIF,https://gbif.org,TRUE Guatemala,Sistema Nacional de Información sobre Diversidad Biológica de Guatemala,SNIBgt,https://snib.conap.gob.gt,TRUE Portugal,GBIF Portugal,GBIF.pt,https://www.gbif.pt,TRUE @@ -12,12 +12,13 @@ Sweden,Swedish Biodiversity Data Infrastructure,SBDI,https://biodiversitydata.se United Kingdom,National Biodiversity Network,NBN,https://nbn.org.uk,TRUE Canada,Candensys,NA,http://www.canadensys.net,FALSE Colombia,Sistema de Información sobre Biodiversidad de Colombia,SiB Colombia,https://biodiversidad.co/,FALSE -United States and Territories,GBIF US,GBIF.us,https://gbif.us/,FALSE -North America,GBIF North America Region,NA,https://www.gbif-north-america.org,FALSE +Estonia,eElurikkus,NA,https://elurikkus.ee,FALSE Germany,Virtuelles Herbarium Deutschland,VHD,https://herbarium.gbif.de/,FALSE -Netherlands,Virtual Collection of the Natural History Museum Rotterdam,NA,https://specimens.hetnatuurhistorisch.nl/,FALSE Global,Legume Data Portal,LDP,https://www.legumedata.org/,FALSE +Netherlands,Virtual Collection of the Natural History Museum Rotterdam,NA,https://specimens.hetnatuurhistorisch.nl/,FALSE +North America,GBIF North America Region,NA,https://www.gbif-north-america.org,FALSE Norway,Living Norway,LN,https://data.livingnorway.no/,FALSE -South Africa,South African National Biodiversity Institute,SANBI-GBIF,https://www.sanbi-gbif.org/,FALSE Pacific,Pacific Biodiversity Information Facility,PBIF,https://pbif.sprep.org/,FALSE +South Africa,South African National Biodiversity Institute,SANBI-GBIF,https://www.sanbi-gbif.org/,FALSE +United States and Territories,GBIF US,GBIF.us,https://gbif.us/,FALSE Vermont,Vermont Atlas of Life,VAL,https://val.vtecostudies.org,FALSE \ No newline at end of file From 24bfef1b160e535f172ffdb9ff416e08ffc27fb5 Mon Sep 17 00:00:00 2001 From: Dimi Brosens Date: Thu, 16 Jan 2025 09:42:03 +0100 Subject: [PATCH 08/27] Update node_metadata.csv small typo acronym should be VBP and not VB --- data-raw/node_metadata.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data-raw/node_metadata.csv b/data-raw/node_metadata.csv index a701fb9d..1830f7ea 100644 --- a/data-raw/node_metadata.csv +++ b/data-raw/node_metadata.csv @@ -2,7 +2,7 @@ region,institution,acronym,url,supported Australia,Atlas of Living Australia,ALA,https://www.ala.org.au,TRUE Austria,Biodiversitäts-Atlas Österreich,BAO,https://biodiversityatlas.at,TRUE Brazil,Sistemas de Informações sobre a Biodiversidade Brasileira,SiBBr,https://sibbr.gov.br,TRUE -Flanders,Vlaams Biodiversiteitsportaal,VB,https://natuurdata.dev.inbo.be,TRUE +Flanders,Vlaams Biodiversiteitsportaal,VBP,https://natuurdata.dev.inbo.be,TRUE France,Portail français d'accès aux données d'observation sur les espèces,OpenObs,https://openobs.mnhn.fr,TRUE Global,Global Biodiversity Information Facility,GBIF,https://gbif.org,TRUE Guatemala,Sistema Nacional de Información sobre Diversidad Biológica de Guatemala,SNIBgt,https://snib.conap.gob.gt,TRUE @@ -21,4 +21,4 @@ Norway,Living Norway,LN,https://data.livingnorway.no/,FALSE Pacific,Pacific Biodiversity Information Facility,PBIF,https://pbif.sprep.org/,FALSE South Africa,South African National Biodiversity Institute,SANBI-GBIF,https://www.sanbi-gbif.org/,FALSE United States and Territories,GBIF US,GBIF.us,https://gbif.us/,FALSE -Vermont,Vermont Atlas of Life,VAL,https://val.vtecostudies.org,FALSE \ No newline at end of file +Vermont,Vermont Atlas of Life,VAL,https://val.vtecostudies.org,FALSE From 916d037ab0d9774be25ea40fb1c440eb49b36dfa Mon Sep 17 00:00:00 2001 From: Dimi Brosens Date: Fri, 17 Jan 2025 15:36:32 +0100 Subject: [PATCH 09/27] webservices for Flanders --- data-raw/node_config.csv | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/data-raw/node_config.csv b/data-raw/node_config.csv index 12fa9ed5..cb5b40fd 100644 --- a/data-raw/node_config.csv +++ b/data-raw/node_config.csv @@ -1,4 +1,4 @@ -atlas,type,url,functional +atlas,type,url,functional Australia,data/distributions-id,https://api.ala.org.au/spatial-service/distribution/{id},FALSE Australia,data/distributions-taxa,https://api.ala.org.au/spatial-service/distribution/lsids/{lsid}?nowkt=false,FALSE Australia,data/occurrences,https://api.ala.org.au/occurrences/occurrences/offline/download,TRUE @@ -73,6 +73,24 @@ Estonia,metadata/fields-unnest,https://elurikkus.ee/biocache-service/occurrence/ Estonia,metadata/providers,https://elurikkus.ee/collectory/ws/dataProvider,TRUE Estonia,metadata/taxa-single,https://elurikkus.ee/bie-index/search?q={name}&pageSize=5,TRUE Estonia,metadata/taxa-unnest,https://elurikkus.ee/bie-index/childConcepts/{id},TRUE +Flanders,metadata/taxa-unnest,https://natuurdata.dev.inbo.be/bie-index/openapi/childConcepts/{id},TRUE +Flanders,metadata/assertions,https://natuurdata.dev.inbo.be/biocache-service/assertions/codes,TRUE +Flanders,metadata/fields,https://natuurdata.dev.inbo.be/biocache-service/index/fields,TRUE +Flanders,data/occurrences-count-groupby,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE +Flanders,data/species-count-,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE +Flanders,data/metadata/fields-unnest,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE +Flanders,data/species,https://natuurdata.dev.inbo.be/biocache-service/occurrences/facets/download,TRUE +Flanders,data/occurrences,https://natuurdata.dev.inbo.be/biocache-service/occurrences/offline/download,TRUE +Flanders,data/occurrences-count,https://natuurdata.dev.inbo.be/biocache-service/occurrences/search,TRUE +Flanders,metadata/collections,https://natuurdata.dev.inbo.be/collectory/ws/collection,TRUE +Flanders,metadata/providers,https://natuurdata.dev.inbo.be/collectory/ws/dataProvider,TRUE +Flanders,metadata/datasets,https://natuurdata.dev.inbo.be/collectory/ws/dataResource,TRUE +Flanders,metadata/media,https://natuurdata.dev.inbo.be/image-service/ws/getImageInfoForIdList,TRUE +Flanders,files/media,https://natuurdata.dev.inbo.be/image-service/ws/image/{id}/original,TRUE +Flanders,metadata/licences,https://natuurdata.dev.inbo.be/image-service/ws/licence,TRUE +Flanders,metadata/lists,https://natuurdata.dev.inbo.be/species-list/ws/speciesList/,TRUE +Flanders,metadata/lists-unnest,https://natuurdata.dev.inbo.be/species-list/ws/speciesListItems/{list_id},TRUE +Flanders,metadata/reasons,https://natuurdata.dev.inbo.be/logger/service/logger/reasons,TRUE France,data/occurrences,https://openobs.mnhn.fr/biocache-service/occurrences/offline/download,TRUE France,data/occurrences-count,https://openobs.mnhn.fr/biocache-service/occurrences/search,TRUE France,data/occurrences-count-groupby,https://openobs.mnhn.fr/biocache-service/occurrence/facets,TRUE From d6304675470d034e1023d5ba58970f15778a7ad4 Mon Sep 17 00:00:00 2001 From: Dimi Brosens Date: Fri, 17 Jan 2025 16:29:45 +0100 Subject: [PATCH 10/27] fix typo --- data-raw/node_config.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-raw/node_config.csv b/data-raw/node_config.csv index cb5b40fd..af3cbf3a 100644 --- a/data-raw/node_config.csv +++ b/data-raw/node_config.csv @@ -73,7 +73,7 @@ Estonia,metadata/fields-unnest,https://elurikkus.ee/biocache-service/occurrence/ Estonia,metadata/providers,https://elurikkus.ee/collectory/ws/dataProvider,TRUE Estonia,metadata/taxa-single,https://elurikkus.ee/bie-index/search?q={name}&pageSize=5,TRUE Estonia,metadata/taxa-unnest,https://elurikkus.ee/bie-index/childConcepts/{id},TRUE -Flanders,metadata/taxa-unnest,https://natuurdata.dev.inbo.be/bie-index/openapi/childConcepts/{id},TRUE +Flanders,metadata/taxa-unnest,https://natuurdata.dev.inbo.be/bie-index/childConcepts/{id},TRUE Flanders,metadata/assertions,https://natuurdata.dev.inbo.be/biocache-service/assertions/codes,TRUE Flanders,metadata/fields,https://natuurdata.dev.inbo.be/biocache-service/index/fields,TRUE Flanders,data/occurrences-count-groupby,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE From 598bd972e22df20f9bcf4b54b612e4e9a7df128c Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Thu, 30 Jan 2025 17:48:41 +1100 Subject: [PATCH 11/27] Add first draft of reproducible downloads vignette #261 --- man/figures/doi-web-example.png | Bin 0 -> 146264 bytes vignettes/download-data-reproducibly.Rmd | 121 +++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 man/figures/doi-web-example.png create mode 100644 vignettes/download-data-reproducibly.Rmd diff --git a/man/figures/doi-web-example.png b/man/figures/doi-web-example.png new file mode 100644 index 0000000000000000000000000000000000000000..715565640cf66fcd4bd727822814feaa2d7393de GIT binary patch literal 146264 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 literal 0 HcmV?d00001 diff --git a/vignettes/download-data-reproducibly.Rmd b/vignettes/download-data-reproducibly.Rmd new file mode 100644 index 00000000..05335bf3 --- /dev/null +++ b/vignettes/download-data-reproducibly.Rmd @@ -0,0 +1,121 @@ +--- +title: "Download data reproducibly" +author: "Dax Kellie & Martin Westgate" +date: '2023-10-13' +output: + rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Download data reproducibly} + %\VignetteEncoding{UTF-8} + %\VignetteEngine{knitr::rmarkdown} +resource_files: + - '../man/figures/doi-web-example.png' +editor_options: + markdown: + wrap: 72 +--- + +Reproducible data downloads are important for a project's long-term use +and longevity. Below we briefly discuss why galah queries aren't +reproducible by default, how to make a data download reproducible and +how to use this method download the same data again. + +## The trade-off of new data + +By default, galah downloads data directly from each Living Atlas API. +This default means that galah automatically downloads the most +up-to-date data available (yay!). + +Living Atlases, however, are constantly ingesting more data. These new +data may be from within the last week, the last year, or the last +hundred years, depending on the source. As a result, even if we run the same query, the data we +download today may return a different result from the data we downloaded +yesterday! Frequent data ingestion means that although galah is useful +because it downloads the latest data, galah won't necessarily return the +same data every time we run the same query. How can we preserve the +result returned by a query if the query always changes? + +## Generate a data DOI + +To make data downloads reproducible, galah allows us to mint a unique +*DOI* (Digital Object Identifier) for a specific query and its +subsequent result (NOTE: This functionality is currently supported only +for queries to the Atlas of Living Australia). + +A DOI works very similarly to a url, in that it holds content that is +accessed using a specific link. However, a url can "break" if the url is +renamed or the content once found under a certain url is moved to a new +url. In contrast, a DOI is persistent and will always direct a user to +the same content, even if the url changes. This persistence is why DOIs +are valuable for long-term reproducibility of content. + +In galah, you can also add a DOI to your download query. You just need +to add `mint_doi = TRUE` to `atlas_occurrences()`. A unique DOI will be +assigned to the resulting object once the query is run. + + + +```{r} +#| eval: false +#| echo: true +library(galah) +galah_config(email = "your-email-here") + +# download bandicoot occurrence records from 2003 +occs <- galah_call() |> + identify("perameles") |> + filter(year == 2003) |> + atlas_occurrences(mint_doi = TRUE) # add DOI + +occs |> print(n = 5) +``` + +```{r} +#| eval: true +#| echo: false +#| warning: false +#| message: false +library(galah) +galah_config(email = "dax.kellie@csiro.au", verbose = FALSE) +occs <- galah_call() |> + identify("perameles") |> + filter(year == 2003) |> + atlas_occurrences(mint_doi = TRUE) # add DOI +occs |> print(n = 5) +``` + +galah preserves lots of information in an object's attributes, many +which are used to construct the API query sent to the specified Living +Atlas. We can view new DOI assigned to `occs` by checking its +attributes. + +```{r} +attributes(occs)$doi +``` + +We now have a persistent link to this data download. If we paste this +DOI into your browser, we can see additional information about our +query, including a breakdown of data providers and licensing. + +```{r atlas-support, echo = FALSE, out.width = "100%"} +knitr::include_graphics('../man/figures/doi-web-example.png') +``` + +## Download data using a DOI + +To use a DOI to return the results of a query again, we'll use +`galah_filter()`. We can specify that we would like to filter our +results to only the records returned by our DOI. + +```{r} +occs_again <- galah_call() |> + filter(doi == attributes(occs)$doi) |> # filter by doi + atlas_occurrences() + +occs_again |> print(n = 5) +``` + +Our query reproduces the the same records as our original query! + +If you would like other examples, we use this DOI method to +reproducibly download data throughout the [*Cleaning Biodiversity Data in R*](https://cleaning-data-r.ala.org.au/data-in-this-book.html) book. From 89ffa93f7c7b6775e5e72debef0901cafbf43161 Mon Sep 17 00:00:00 2001 From: Dimi Brosens Date: Thu, 30 Jan 2025 10:49:04 +0100 Subject: [PATCH 12/27] Update node_config.csv for Flanders add dataquality webservices url --- data-raw/node_config.csv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data-raw/node_config.csv b/data-raw/node_config.csv index af3cbf3a..05c2e954 100644 --- a/data-raw/node_config.csv +++ b/data-raw/node_config.csv @@ -76,6 +76,8 @@ Estonia,metadata/taxa-unnest,https://elurikkus.ee/bie-index/childConcepts/{id},T Flanders,metadata/taxa-unnest,https://natuurdata.dev.inbo.be/bie-index/childConcepts/{id},TRUE Flanders,metadata/assertions,https://natuurdata.dev.inbo.be/biocache-service/assertions/codes,TRUE Flanders,metadata/fields,https://natuurdata.dev.inbo.be/biocache-service/index/fields,TRUE +Flanders,metadata/profiles,https://natuurdata.dev.inbo.be/dqf-service/api/v1/data-profiles/,TRUE +Flanders,metadata/profiles-unnest,https://natuurdata.dev.inbo.be/dqf-service/api/v1/quality/activeProfile?profileName={profile},TRUE Flanders,data/occurrences-count-groupby,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE Flanders,data/species-count-,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE Flanders,data/metadata/fields-unnest,https://natuurdata.dev.inbo.be/biocache-service/occurrence/facets,TRUE @@ -210,4 +212,4 @@ United Kingdom,metadata/media,https://images.nbnatlas.org/ws/getImageInfoForIdLi United Kingdom,metadata/providers,https://registry.nbnatlas.org/ws/dataProvider,TRUE United Kingdom,metadata/reasons,https://logger.nbnatlas.org/service/logger/reasons,TRUE United Kingdom,metadata/taxa-single,https://species-ws.nbnatlas.org/search?q={name}&pageSize=5,TRUE -United Kingdom,metadata/taxa-unnest,https://species-ws.nbnatlas.org/childConcepts/{id},TRUE \ No newline at end of file +United Kingdom,metadata/taxa-unnest,https://species-ws.nbnatlas.org/childConcepts/{id},TRUE From 611e1724176741b424f2ede5c77b12b55a66bb18 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Mon, 3 Feb 2025 13:18:04 +1100 Subject: [PATCH 13/27] update `collect_fields()` to filter out entries with stored = FALSE (#248) This issue is largely addressed by changes discussed in https://github.com/AtlasOfLivingAustralia/biocache-service/issues/930 --- R/collect_metadata.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/collect_metadata.R b/R/collect_metadata.R index 39347ec8..e33bfb27 100644 --- a/R/collect_metadata.R +++ b/R/collect_metadata.R @@ -170,6 +170,11 @@ collect_fields <- function(.query){ if(!is.null(.query$url)){ # i.e. there is no cached `tibble` result <- query_API(.query) |> bind_rows() + # if there is a 'stored' field, use it to filter results + if(any(colnames(result) == "stored")){ + result <- result |> dplyr::filter(stored == TRUE) + } + # now mutate to required format result <- result |> mutate(id = result$name) |> select(all_of(wanted_columns("fields"))) |> From 3c9b7522a151b001984415367acc7f5010ebfebf Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Mon, 3 Feb 2025 13:52:33 +1100 Subject: [PATCH 14/27] Temporarily deactivate Flemish atlas for release (#256) Reactivate after release to continue development --- R/sysdata.rda | Bin 17682 -> 17525 bytes data-raw/2_internal_data.R | 6 +----- data-raw/node_metadata.csv | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/R/sysdata.rda b/R/sysdata.rda index c9f045cd8353c6b479a14997a45c02897fe87754..93694ccc7ebb9e6539e524e6dd55f94aab9191ea 100644 GIT binary patch delta 17416 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 LaG?buv*pK}-WB&} delta 17614 zcmV(^K-ItXhyjv{0e?bTXgM)KSte6iP1TQE09T-Y|NsC0|NsC0|NsC0|NsC0{!T(c z07T(lh!_9>5zJG(|?%dyZS`-+fP!Om+ z7z9*@-%vHk+I?%-)CoZiz1eeN>GsBsQZVipTC~b{-aS&g-OZVx54gYxC#FKZh8hec ztCL#p#+Vbnd)?-gDzCn*5jw9iT2z8L8oG3K3X-C$>w4>O-+hkyfRrKvX`ld@h7bvp zOieN}F$S8L6Mq6~ctqJr;iVo)>U&i6KT;m1n?a}p5$ZORL8gF#2na@vL6b?Ud6H^= zlwqXP)C~a80017LpvVEHfMfsw00T`lK`D}YX*C{G$~{9zq&-hfOw`Z}N2#Cy8UO$Q z00w{n0000IX(AwjG*PFis(N@TY3b@i)G`bL9-sgQfEqnO&;S4c00@+X znLQ?qhNSe=&s3XCCV{dvX{5%fwu*W%ntCCW+J}%eJrhq*WYB14lSilk01X655Fii) z!5SkGl46@p8czv|pOK)RhNo)V~-UQuGP>~`{R3V#Iv>+7Lcucnb+Y}W(~c=C-oZPgIF z0TXoq6X!1uIBp~GK@g&#pZSC5c=vc%bKNi1SIu(HOq>*1n>X3?%M21?rA_pK1n@)w z^`e4$@f1CHDxQdf9-fUXpq^A!Jh*~+kwD==15yH-u|VO)P9z1^f*e>O#(?X~AwB0Lzb)CZdoInYm~0O!O!_z$56p%m!AdQeWB zQ_+dkf$2cogVl@Hf$784hpPbV#OQ77g+c^iuPQ2g)dd6Ni$({{HjXLA=@kU)#vcVM zC1fk=7EhEO!ijhwj?v9%gP8S%C@5rLL4S~w@6A3+*U6HuYDG*3)`)?){to3?CGUI9 zDyRK^6x@IC`(M9k{cx1$W}>cDQ1W+=U)^x6N8ZCN#m2tP0S{_gksw8UVV*`RVR25W#*3+o}pB#c9v$sU6!@%Q$y@$Tc5k$<< zUg45U7h|nlw?;_LbDiQQBDdae7n!|PUUlR88}IEt0P*a7ztbc)Ko2{#e`55NECzz z;y#1Z(EAGeJpDZ?J+tY1#6%m!RWnsh?`fOWRqq#DS{l~7L(}(8L~`S4oPXn4+go1u zz3+S6ZQwWGt9|8fe{Q?>v>W?nZx{59O;3mDd1h35{e%QJ&(n@PJWq$8pQ)qHWOnQh z!#i=(Jg2L=gW+)3iBa*p8iocyA7{EE5Bm7{|8s~4hQcqAkeo*20uh@9ytzYNuHDR0 z!!haou8jW4tmg9?OMHZLKYy2wY@Ihz@AxVr00+`SAWw$#kP{8Sp6|97VH45)!Pms+ z&oph;d)#K3`?Ijyv)cS)HuX#YLqBH^g||=wzTl|4Ja+8VB~(t;Zb95(ox!xr-!BzB z*v&pd#O|U_J3GBolR{4CS;J?6YR^wMJ3^nZ7FynQ$Nw}NqD zsGA9lMd0ZrmD@if(>z`222-aaOR5U^{rr0JE$zE!bO0Yo_8s^YvkSGPCdddZw2L!M z&^TD5`R(K{=nEWr%VCec`W2t6@dp?Ky6TzUmg;#*mna9DyE?;%%fpk(LdU9}mbox9 zeZq61>_(S!6wT;rWYD`2F8& z0bic$N1oYMj9qJ}q;NwyQ7(ZU2Z|q`!^$SQnmtJ0l#AO}Aj7vM#)~Dr6_|RcWkW)>jn#fcLns|XA=*9=hW%ApV%72HpnZpQfR_}Sq4arz~ z1eVvbyuDVTbx@r_>x3j@j%yJXSG5kGPa@*?^^V=KjxB+_1HYNbz88+4MY_-2p1aP$ z+oQ7}41lIkfI<#(9p(v8=ja?S#x=MgUGgK~qg=MGaDKW4@7K%3Kr>j{eE6`m3Zs`7z7yWG0Gq>fdqDCu?%w z%ynVXyt!wX^-qU=Z{e?2Lo?cT@m{fd_j@^2>#=pE@!L*3!QSpHR$2s7w0U7dNplXY z{#Ab=U5U&3`1RV|WNyCSKZEf{UHWTi=zPbn!n$+iwSW2ApiIs@8d=KtHtpg@!)s*t zdgF$1&u?zCpRsy4jSIuYVmx@rd~4+SE2iRh`Pal_#k*(lhlR$Q%KqNG|IeiDo#&Xk z!p~10A^Ui{+}}5~{bM3t{TBW%HRT#WG^5I1*2uVkxmz#gVaKRfIw!ePPB%>f?W+2Z z(2$wh7=Py}mgSx~Cas$@*n2vzV_1?2w5YI--p}|iQ=0g%W*)WUGhiE+$j;9mUdbNX z{2N-vK)XmOl7XE&_;TeJTi1e!;9`}P-gON8`fT(0HFukJ?rk%ZI9vm{U_~9Z_}7uk z@eV8v`mwE!gK-B|H5<(avrMQyp_~iIj2;!Ojek%Tzwl~#TQ=u6gVqCJ=xy{yqjkFj zIOkX1Y9eIOUA0LE2P>8haI!>P-Ii?=5faR$X(Z&qaR-lU?e*StHnVN3HOH*GZNJ}) zz)&ELg&9}y^8D`}qTZXe52a9Vbp;r<$oL;Y>=?dPHOa+K|EBevI=KvvSzfd>Q)y+ z+oJ+52=}2x5K7E4iS{W)P(>qalX#%J4TltCZ@H+=2;x*gwdL0gsF~9+IRWZ~ccxaC zm^$JyrcckI6-jHz6F?U0veWYv9+EEtz>1Jc8=e70+^a9r$=@QxbjR#C@6zeC=zl!@ z|3O2ss}Fz_l>(c}yN80-yni-r<{hM7{|(%bPpd(N$B2uF1QCVfm8bM-d{#*^2e=TP zeAkp~I2FB0dGFXG0n|nYe&33cwA+j92al_k-yd5b+XCBkvYsULT=RA?RY?+5V0bqn zpF9Tyj(si&w*UyqR?d?uMiBr#*MBkz94esN;{kL(Mx011Sw!v}KiBX5m^t|Ud}DsY zsT0iWpn=r(`0LPk@s2(&L${-jp1AYeM_v7r%-C`kaqg&PWfa+GD`NsHFx7O*mWgOf zNOa^*)48d8vpz+l+xc+ihEpEq`$LzvlIql$z=f6v+1Pwq_l5Op!U*4i;D4nfYgczP zy;>QADz0h;Bb}L0al>lfXe9Z0%CIE^BFin{IWFGe@q605GBDORmJl!ATtCBLL9ZGS z;1t=JMqFuMX!u?%u7#Qd$)FtQR->h};8cl*nI#Q|_Kghq}f5+lMOFq6Jk z?wc_RKOp#HObHe_BFdo`asYs#SY1Gzn~vmBsU3F$F^{#cBVs=v3n(ic0&*7fNzOq2|HuNlOYT%f6F@%E!k~a9xMeAG7v;sws6Heh=_q?WWZNP9U@U7X{25D?4Vq1 zkHwacjs~D*@=c#Va!Vi41rO>AQC;I zKoFN6C`-b^Fi{!{+)7QVFs1=st&%8Qg&mSQf7n9B?#!g7QFa@oH1Nxp z2#v3!UuWScH{JxhxFGZdF6tKa8E5Grl#POrXrYtgw8zWhmCvq=WkB$@{fwdpeVjF@cQZH_E6{Kul6v?`xSjbpqgpHxLvQxmpqxDzix+bQZ{2R4SoCxNUkl829N? zN(+S>;k1{Oe~?XgHXLilca=~K@)`}rhQi#&#)uRF;1ubt#12ZksTyEK%jgXhQC8n! zit;U+!ZL{Vb2=Hd8U!&I(b%1x5~6lc>J)s+8875MtJxt$B!+7sh7k4jx?$8Pv@4@J z;7haZaat~- z5b|CEh6_MA!i7k()dt?kU)V;{F`0G2*7yOK$DEXHWPHs#fSw&g_T1j zFO#CQSD(Ug8n25dA~z^4$v9>~hXF%afp;8NxyyU9lY?Cu|Z=vVE9*n0&_Qpn9r%`=ce)ke`ymsMymArKMvPLj_9Y z+vT`>eKa)|MU!4b@`P!knu1pS<*=+d(9CFlyqc7Ma4HummNIG) z;KNj6i3}5^CP_0HGwUUBmBxa}bVgcO*g-i7X-&0Hp8Epk(_*DZ4J|CVA+c4oB&8_B zO!$yc@(paSn+38LBTm><+R6?Cod%Kg24Vyr^*#!5C#fF(j5&YU>oWfkaTklO>~iA zTSAq<4xopk0g!fffby%_4uPrGYBP(jEt9MeBw?3kSRPMkMNJF@hh6RXfdkv&;mx+& z=`w@i1H$do9h`(f5ifA>p34xsld{NYCD6=t=}QIz6ktO?W3LB?yI%CMwwpNaYe5kd zgC|HFz({QAH4dkcY%`W|B5+vd#S#G6AhxtLaE|)gbTKi_2HLNk8Ks<;MR#8NbgjslRu*#Z15AspYcz_i*0qL!sco`ln&6;hDz1xxO)|lCVt93A zC?ck!+)@POMJ)9QH3&j^$`AFqK&$Rxmps%Lr6_+$?EOB1bAxSfey!Jk@p@&62*pE} z>fD2C^=|$aWlbR|I5N2Gdt$PI3^sH| zW&Jfv149)ZsBg$`*trf(JOUgJqSXn-Xg6HU09)eEI~KLK_GA2iEcz%*bb-deJ@_xc zp=(ek6n6&@kqQ!f2tb2MZdmjz7>|;2Qh^cr3l&5qLVlqN*Pi ztan>4%=O!o?!9iu#lyt%_HS$Q>8cvsIsnoFNRWaaZ74zrNU@7;3Y8Ivj6wS+>PTQF z->yi00ub-{0aig|5(xsys8c%x0!JboR0Di)P(TO-=+Y5?di5y@3djINFf)ZfWEAJA zcdA$~`+21@t|M`6p&D*p<*?nQXqE+XgI&$pdHWyDh~VKMGNkw#?FFe(vQ)CF=zyiL^l@^pe7faaABO_6y(!!+|_|d9ve`L2&!_( zMp(7a&FB+w*afUyh;BqatWp@upI^8;>pi?}}l z%zP&y`k>+|u-OfPs5dxk5*(_)EtUXLaDS36@Zo^t`-C}6L)dI+n#C!|^F5czCn`%KXiQrz4YF3skB*S4!>suj-{; zO^-LPyUIj66N%xp-MNT0z#eNIJ;gv8n_(MFaLmFG3W%aADXHMhk|5d4!EzX)LoEfU zDTH6;*U;iBI2?2hdv$bU18&b5&KMjMhZaVExpAB8?t8_xf*Wx(WD&wc0>=a@U8Y zmZJ+0j+2g2RRF$k>-tCZEGSkaufUuF6gI%OmR(dip`;C>T4ut-u$h|8+d;u_b3pFv z9_LTPh{}nrubaa^ZX70&Bal$R+qVC6W|c4Hq>zN$=6edtD54S3gFqx>63`%jYCBsj z`F+N+g)jzhWLKIatejGQa}xz}v_usD}L) zI5xKG&sMI4jg?wV*tu0FGb7o5!{E0MSJkynPNzdW&l5pgJbV2D2kU$#LB#YW3u;KY zmmGq^e8dYfvV{QP^-5b~bX%jV0^=0x6_=r}yDH2jtUbIMm}-EKOeAs*5(t!?0g?w9 zf!6@x6_FC*03<`C@%@ARA4R=;)qQJx7oLLE6s2f1_tg&kf(vZ;8uzn*W5c3%bvN1V zb)4~Gn`>ivmr@B;D6YuDt166{*)#=FLy;p1&nd7&;^Z@s;0V;Q@r+aC>9C$vx%dV< zkLcC(p(Yo&MnVXPCt6TQf(VS57FCAe2jvXSJztLa$xpL>h^?0m*nS$vGiXD*i_rlo+by5`)r)S9dUiZEIL#T1-9V>j^cPCp6!cC|A6 zevN1NJ?)sR%plmhdA%pqQ>G{$t+ews7VwdmbUZu$L`0!t>s(xa#d5Owm6qbRx*o50 zK1_4rwns0xDeq+)L!e)0l3s!hje#E;?w0dyE)kl(TsPp;>AdnoP~%!^ac4iwxq8(T z_hIgaW{19O3NUqqTM_z88^M5TqYyL`?3~+5Fd-X^8wSR#K7llAGF(hVk4IrGF*GYP z1|{qURouaBdZ(*@xZZbYX~-VqyZ8C--a4mGg9ZYEI~z=|yuuz{q+S>3^eVc#=&MY_ z#q6tnv)7tv67>5_=BkSL=J8K!ST<+G-L{M$Ba%yo;v4Dzg0waV#qR)`@LI+~p1hPZ-Cmc(&4^O}C`6_Q%-}<;A*!n&_KDX_+;L|?;-$IXL)0umnpZBVFPld56s;YR#VAx&p zPe(_8u_H6jTRmRrIH2sQrY9^kjeOwPe{2reF$$Uf1nlY|U#jsFzvF-q&`pQg*?$3; zTurx-h9>gGf)QALZ@4{u-*czq@52g*HJ$#)&-aU(U{}ras95Rxd=|xoVZZg}?&U+! z1%C$$Be2~^334Ha#iKG-os?@`qZe@Te0pSmm8HPu#=v6lsEj%Nrbk@mJD&Y#GAOl$ z7%l~rapw>O^0GjsGh09B`04l^@~=*#ceg(zP{9GxoTs~@Xrck7wu*ZOQAHF!H!*AU zYM6Hm^CFSulX zsG8VY~m+C z?-*Xv`mI%K1lTeL))4uD_%=`!Rz-sevUC%sq?)}%JBKa-v0%`!XLd9~bCsj zpimkV>0yK$kU6YCDpNoz%7qf_r7ORGJjTH;C;{@@IPpv|t&VpRiyzkv-c1ZlDyWH2 z+;YNp_vU_WW+V>1zvSfQ@fHcI5wKnWS_yGr)Fu3u=Zj+WL5b>kr7NK?<0&;nywC0b zGR5Fz#gKoW7ij?cPdz=v3`iam1hgIyS0XAwe|kz5#_B`Yac%^}^ZxbBc(f~jFp=2` z0S>9!lnfBEG9wOi@Fo^?;Mvl10SGXi;6w>nKm{QOIKX@`!V;{N_wn&_$;fl!Y>uy{_;KrG6geY63nl!G*+lbNI=3ZP3Bx^Pau<(@0JBbI9EFq+06B=^$JCbu zTlWDqNeVh+CtjoqSx|0_yAH5_?Fc2?h67|VA~~4(PIF{5<(iOTwSs564*rm|yr!rx)ILn~{FR0IbTlM}VOb+_a4*r!RR;`>W*l69gux67mp_G< z5j@b)cq&bX{k!wh#T1`b6-Ej{MMeV#AqXUdVnKpQOczn%iy;`7vrIfuvaIY%uqGMgw}iH3<;#n_qySl)?a10(BOl`izy~2=N*iVswFoRKpm- zgfKX@dPoN%;kI{3jxUNwM4VT7m><^mADoj(GQ z00MaX!UYAOz#K(R;D}fQ%X;-CRaTWtliML6e|_(ksmO{Q(}QoBfp#*(X-#}^50{hU zMtON(2zWDYqMHdSk2-B1CeVr+Qul!6q`!&P4N~zGeJOWr|Gg&$&=p~Iw47@)nwIcT zFhj1a$9QJ7#`w++nwee@L-YmX!joDr1^p~J(aBD^I-N)$c>z>G*L`n}oL83un|{}< zf1#jQM{sqX;j|Xkkbq$@NQ$gh zFjgRuL1a=Mo`hx?SUNK%Vi)c@Ks&%I-kZ>{zYQty^GyJA5cteKU}BVpJ^_bqB<5QujuVt(4#zbnjOv|NFG z{;-5V(@}Egi$B4DI2oEWTN2g+9hoM9e^o64EA}#QQFsTW@xY9-cw&YG*dTP+vX)4` zieJgC3zJFSXLU0P;)rLynjs)WKtxbdG1sZ;3$qD?3(T{rIoowWi*F?5~8{e=mO6c2z_w z1q@UVRHB6}1SAF}rJn92!GH)^C)U}i^3qUI{I(;Rav*zo)ywkkoH`CK4z*Ec#)|Sz zM-DZN#bXs=iCUFM8Y9bZERxO-qmwk3mc}m>kgg8{j8<|c@@dpg9Uz=G6M+L4} zdACC~I@ku}C9qYQHYJ^)!+J)Ygbwk<%R)4>Qg~3G06Rc#0H0kC+7r1CA$3n0{}Boi z?CI^-wGDUemO|BOZNoffH^`S00YlAh)6HDQ0(xyp&EpRbRGf88e7Enp-8Ma2GRriLJX(Xw{Nrh1H~I9(&`0_ks_8+SewEKe@zp;grN4yyWU}`_tN zA`Jug(`G?y3JZ|0dJ@*kTV!cOt4*v#Qi>D^bYY5Nn2VuEI7_Ow&*{O-ON88v5HJ;UE4$_egpGz(D#)mi4TUOXpdS1em&1<%;a?%vY&r{} zCqg=Lo-90IY^K>*R6|dWcGUiWGlGa@ zc#oJU4u#LIgw>n2mYJ)Vk<3hG#Q?Y z0?-4GJ6^%-&u1yV32`6>rKn#9z;tnNGf$tF5bwxqlT4LIBO*X)zySB5;(pL7AjTrA zBvn`t{K$BZ4V}mxfLkWO)TRNlE0h$F8$Cypf5>QlE-P4Y%Y$)zv1!4O8P8KODk3VV zs0OYwYKsuTuJgW3=F^Kejx4uB?^ zi{XKZ#XB5)&m`d3Y|s+01Fbf0@5aN()Y&&ysbWc?R=J!bmLwh?YF+0@4)IR`2m(bt zfBE!m;S4#yQVjBgJ?J7qKtYm|T3q@IoCzI1qRE_r2_VE9X7y=)Uny>-dyymdfpujP z5-&3Ik&r+^*{3A-BF)F*BeZ!y%h6S+i53cM5S=4G6?Jd!U=jo(9uUj%0OoVS+$qfr zoQquAQYA$1n+dvCf9{qKA4%f^wg^T7f5HH~wgONnW}i1rAF0d{gTIj64Ujbh>DWN~ zxOD*eW7h9I88vC;N>bGg;%2&J)Mnvku+82nks@rcs!Cd^vY6^;p%a}1Gr!}b5S_!| z;y0lQB6|?fu|$eMSJs6AiZIDaiS>MgNIuDa5m=yfN1>72K0Ce>+2(%z>iug;N_q874-e5ZxqIC^rEPlr2uhRAfYW zxQK2tR*vLo+kcxj6OoZCO&<%vQ|1}nH$96lcZ3!<5a$Eg>dR*$f}?TwO4^e*|W(y`yCPsr25^VJb~^!xCgzJuAzY$ z4}t67tbxD~IH?xe`8}>@PLd@gk|dnCqH}UONGCvtDamvdVLehwB2q|{i6-N$8@$|F zJCTCM!M6iP6FGNaG{93?e-SDk_>352_Cf%4u+W{u*ViBzm5U+HC8EV3Ld(bLEpUKi zs7_%6Lm|3&Pft9DLy9E!#(;snKDSJ-Pv7?NmcZ=cxH+S^dTvg0bO&m^wZ(416Us|K zKRCI2Z0@fW@vs!zrm^c}kadgo9z`UCN30^SAdl@R1LCkov}>k{f8r1^55iT^m?gJiDvoG=PsOm_007P~Ttp383yBvrb|lOkaE=i` z$I@C5h#;{b#b%Sf&j{gQtlWt!cwms zUfew&b@%9|XX3L~n7VVL;)3RtG~=f{x|UM9*kq0!Y`9JDu8opA)W^9gP^nd{YVLX| zcl*1WhX{N~5=4wnQ`#&#ZD2d1{JBsvC_{~Sdj&^dp#=s`qBQ_OmJ-QA!Z8GFI~+BV zvY;v7e_&y!_=SLQ9DYCt+8}WR{kQILKYk#c(LX9AdP58{7NIie#%o3zhz!Vnzd$pg zXl(HVybCG>P-&O~R3YsWL8nskn>rcJ683L&3Q@~2eM8tki^qrGqx-dVElr!k@`%BZ zPRNm9Wa2zvR_$yZ?ag;Rle68Exl08q;{^*ue=?v*CYFY5sE6jAV5|?4Pl`?kED%9e z7{x&(fFB721RX6kN+o0lL6BiK$QTphLU*ZF(yI|ci6bo}uuy=t2L!Oefw&D69`@-C zACegcWQax}d_(MmJ0$^>U?VpWrpjkVu3Enu6(t#=gr6dK?n&h}1c;JdGvK|e0@coM ze=c)qo#77Mpmt+`k*f6vfIx8i&0g)x5!_Y*mSLlP69SSWR+;PfJ&KV2Y+*d$AH_NV zSe;kvC(}QqePu*dRaP*>gPK-2;VSzjK?s`ABwD3#9rv^3Sn$Efu3Qu|OqFla{i1HB z#xW%l5j}4obT;D>u;W_VPC=T3BtQ~~e@VN_tMWC>!;=KH84`66L>=>}E#yG;0T0PkwPkfSc@W+e;FEr ztwHr|29ZIcB1j^^aI~sYvPB{AoE!*olRg+sAYkYY@*m3w2e3 zE7vWl6v?Hk;vB4OEzd4O1d)Rz7)c@tN!2PnY@mT>shtc9HQeb*y-kFNA>PfQB1o*9 zW(J{O20||QQcPV#sL<;q63z72@%8jEFV&Fa7Q`Wqm#TGrlrtzF(41Saf49Wen!PTy z-j>E*7)Y#{vm7v(L?EW}IiR=JQJgdkg8-i&Ce8*B~m&w1 zU?PY}p&=kZSSesKiX6~17b1aAHfPQ1+&H?*km{hGDf&2^L6HlHoBWU@2^b^-y@+=_ zJ}$vRt8tp2VDygOje=Sce>g@2uIKbH6S^FoRYBcX1f{0sLAgzZP#M{Na=IT#mbI6z z)wbqX%a~K+wf=z;0B|x z1J^Lngg)7ju0k9Kl}|=T$Ma+!bU4o-Cm)+}xOUv!5(JM&9$yUkrL3NDMeFJWN?Ha zT!@*ddHs*I^nJgh(sJj+VYlo79+4vS`~18^^kDL*C&_ zd$=z5uc;2gyZJp?Q``>u~~ zUxn0z>TMLENi;-~f+&nUP!rKQ~CLp0qVQUF6$XFwXjhk@Mh6a*g!x9=;qHQRZ zL6w$UEKMaasGm9&Oo2lP>m?Aa$%T*&MS=$zV8&rLe_^Zq{Th=^7_8b+DRvBwN-cw+ z-(WENg#cDGW$LT%+xH{3CNPp~q_j$c&?F4vjY|-o=Vk_{CjpKr0zts2sKQ&@3m}FR zUs-P2u8v$}LS|*jp}rxQ6)J={0CBu=QO}@5%eQETXc&;tsD&~RLO~jUg(Q&&KzX04 zMqzP8e~^qmq6@+L-O3dRa+6tMiJ_bg6D%7{Ea#3y0g6EwY~aj^GHz-ZLNQ1{V7|e& zQ}F~D3nT#U4MJj&Bg4DdFQP9&rDkMQg z0ZE+35m^;Q0br=WMUW~W!G@`bMK&-jW{F@}0Z|YU$gI|6*6^27py7 ze`#XO!9=8m(A)+cJMmAS_G}jV!BmNuj<#zp4Hb(uZ0!7FZuO{01)w*_$BpIA`z^>2 ziu>`(YgEv!XU+wvx7-l=SvBgh@}DP!0>bhb?KlJa|B&DSY+Tq#F^$w8DHnT?oS!CY z*nk%d3a+qdnZg(JQ08}sRZ$_%9PkLte)DAmWL> z?+_KGB5h0)Xx=5nmvlcYAJXPt27^W_Yy9O8=i5WKlxT|-gh>cN5y~%`?*oiNl){b~2bb>lo4(KE;A=bt)+MAne+|2EI{3$gh)Bnx zP)jZuVF1n{K$=3kddTTyL2L_zdzU;9-HZ>y;n5k8V`C1U+XI-BgRn(UfYIilXjil! z!$@=HYKbG$USEc~`B&0@bMa{ZPnbDuNP0l?EP_8<&_Ke_wE~aWq^2UH2%^-f_$g5s zp^d2Dyrn0^l^`q)1~U8X5kEtdQ#mt#Aj=KRTT~i`uQ^~Lr@CijLow8yg}(DaZ8fzR z+O1-xlp8LFAmr2c72X5z8DL^!p^?GDGE5MnLg`8lMS8sv4NnG!UP1l`ZjbceLtleb^neqySB$1^n7 zsMzwxcK){!w%k2(F?i0@{Vi=WzBv22m$UM+tmbp4&=AuR$be)ND0uTAg(4xrgi#eB z1~MAe0N$A)etltv^un3)iVkrI-19=RxQlcU$~i(OPN1fc(uAC711LnJ-(aUPPVf`UQ!frwTF zH5oLm>Ln(l01g1K!5Nf)h)F^eM5aYlWT732$zgAiKqcPR8qr~iq=8(Cr%LLw#se!5 zjlCzK$;7H1BfTE7wiuarx>`>WnQ-u3=;fra2#AOdL2&sWu@vNnQBb5v#PLqj((|&! zt1~k&l;v2WqOOSq)ZVW)aYP;XPf{<0VG9;y0*H!j=1>U=;Ev0GghqBWOtUaS2<+n~ zA-kAoJP_KeKO?Uj@avH$7DFM3WalqedSV@EyH`rfqQetb-BGKf4J9omBdbmjeBL#z z<1ort<`@ZWp;lbhQP$EhahApECtY6sigNPGZxQWH4k^Blhw*IAxt6^P_n3|(<~fFM`cYqqdO90x)4JL04SDJYM|l5R72D4E+!e8 zkOmBscX&jAq!thZs&jRxl}!dDiK}W+nqkj^YYV?T@H1P`bk!t@G8QoPtR<|CQYOUZ zIyEsK^qr&x%$dzhsRqZ=g_8n@Q;EtAoFdFjL@Gldj^HwX66lJ^SPWnwqM$^CAqd4` zATU~3Y8Y1W6)D}!zU4f8`)T0fVbFK!Yi`pqB*IrP{GM+<5Zb=J7Hvj~etL12jy2aH1`huB7%q-l6* z!^Brkr*}Pn?JIQJR?sl6g%?2F`MIjlFxLV?LtXw#-EWIhsyk59^+yrZT!Qi}2V0QI z!5n0SiWA)5a{PL-rE-8ylL#^eN6ldXl+Y~%g(C?q0wDrYb||Q%T!g+MoJ=J~M8yob z!X%B@5EQYugZ5ngv1hO-6G8`cGti!h4ceO^a@e+iu**^<0ZslspLOS#0L+psIG{oB zV0(W7vkkp)gmK-(x|eKEig(=&(1oae)_Qi=zkt-9&yzbE4@uZJXnkK}Plw<<_*fze z#bS#5g)D#o$b^d2hD8Wda3x)d3_cM>V-XNaXe3htB%c}vMEXUcVn}myVN*sTgta3x z3L|ZQP-us^G{aeemb_#WhYV~(KZ~CZhG33vCgC$AHk&`uU@e1tRw4wUx`VZAJ1AT% z$k;R~T%b3Qg7)-zVb^1`M||qOI{3bRy5W=CooJpU9tt-W(z)Mo<9fjFn&kq#I|qxS zE&4KTl5cR9ck8Hd_-}y(O{X|Q^zrb?;i+ch1VkAR9yyt-_*-KU95lT6UQ#J_)gz>; zSuh{~wCpD(6m1c-1;Gj(0@ibVnnfXWS!6^MAW;gHI?#wzC`IidNEIh$x3jY(@Eu`) zR-98|*UAm03@n@Jq*YfbP&TaHhT}=#BS=vl>eMsCu56l-LX)0#Fwh{14PjHoo;lJG za;hXL6ayBNh=5Ph8M2)(n@DKrajX{3U?DfMa%43i8w-fRL~lc643M#4GHQ^*ECxJB zm;s~&Tl+?S3DXaASNd%~KQ|sz(`Ot89LNBVe^V0FdZxf9ED}Q4T@VmDVEEt#qOXDL zz`P~a@bJtOBGtyAO7RvfS~Cvny~Z#wBWRHBE7X}O5FzLK{-B=(+CApz*a+JM7)UoC zG6B0l0}RkorA0zFq`VcY0eFq%kdh5(Km!qj-R+>6zNe$MdM(+^Vv&zMac?>cfp9nC ze_4{zBkLAuclN3Q97zg@e7QmtmX3A{LDhT@Y$>^yaYLJ(%$$i41GC3SRQCbb5WvJz z@I*0H&)FxrsY)=!h!DoAjQZZG^&cP|Tn%HXCTi&uTErAshFUhs1XM;VK&>fdV%Sy^ ztyQ+P(loP%S4l0Uh!mo=VAXic%OPszf5;hEG6w~cjFLzrf&hq2?>ya zqB+QDQ0CCA6|=@?-$alwJ^ke4q2GhVBtb0^@bz(_hMY3XKs94X?|^Ix2rUkX=pv?P zuoCJHkP0UBBBqA#d)&Ln0YOR10r{b*K(Qyj2e>`}iS0xD1mtOEp1aZ^Ju%{E*B$be*wSgR%=pdtb?3Pc$qZahFpbWk1- zRJU!zzS(L);{AW{_?@T3uw#3~BE~nAA%D7o{BY!ZV<903njd&#v|1!0>W6|fbof9{ z#5cnTK!k)zah`caoJe_=sH9T_}%0CAm94L%=Rv^&lV%v|7~sQjTJB4x($ z?~H(*6gP0CEds1j2}~ciDnw%8Viq25#Qb|v+CbB03Ldg7??G?`dm<^M!AtG19nU^S z6e=W(5S4)@B+kURmp~{-iUuf|5(dK%$2as0pAicHKItg48r$SEEQ_waru};0;PJQiYDyT_3t*wn?FUm`Ow^l!~f|h%6~of2yei08BvmxOcWu&`6UfCrc@yX^sOA!cW7PfF2_yr~&bt ztJhhu^B$rPd8`~2WD(J$z9L;Q5%Cf#5-fxu5=cl6;v3wE!|X$j$k41u!x&8fvNT9Q zF_e&(=IuP+bu)$e?>up@ud{pS4C7gap)O#k*b}V5NSg`J5EDIBe+C>6UzhnSV;yTja*`zaEcnRU7k(tLjISd6w9%a+z zsB^bNhHs42SSDV0;lVPc`_jVV91d>`&2;B`@w*exbw)%mNTjEXqm3n{Y``eOt6*T+ zx9Xo>${k##0QLMQe{KV)FN$Jb8Hn*5HiPL%NU{V82?-eq2^z@!+gA|j0N4j$j18h4 z1{^TK3x{a#!93+5;|?6qkbxwF5;+J}?Sl~Q{(1rP{OlS93aL>gjYiAunZ6dDj@JIK z7azxgzQ_;&h#+&_l{HN=Yr2kmSP@8s2yr- filter(supported == TRUE) |> select(-supported) -node_metadata$institution[ - which(node_metadata$acronym == "OpenObs") - ] <- "Portail français d'accès aux données d'observation sur les espèces" - # configuration for web services of all atlases # NOTE: # Australia, Brazil & UK use their own taxonomy @@ -109,7 +105,7 @@ galah_internal_cached <- lapply( function(a){ result <- request_metadata(type = a) |> collect() attr(result, "ARCHIVED") <- TRUE - attr(result, "atlas_name") <- "Australia" + attr(result, "region") <- "Australia" result }) # lapply(galah_internal_cached, attributes) # check diff --git a/data-raw/node_metadata.csv b/data-raw/node_metadata.csv index 1830f7ea..ae386881 100644 --- a/data-raw/node_metadata.csv +++ b/data-raw/node_metadata.csv @@ -2,7 +2,7 @@ region,institution,acronym,url,supported Australia,Atlas of Living Australia,ALA,https://www.ala.org.au,TRUE Austria,Biodiversitäts-Atlas Österreich,BAO,https://biodiversityatlas.at,TRUE Brazil,Sistemas de Informações sobre a Biodiversidade Brasileira,SiBBr,https://sibbr.gov.br,TRUE -Flanders,Vlaams Biodiversiteitsportaal,VBP,https://natuurdata.dev.inbo.be,TRUE +Flanders,Vlaams Biodiversiteitsportaal,VBP,https://natuurdata.dev.inbo.be,FALSE France,Portail français d'accès aux données d'observation sur les espèces,OpenObs,https://openobs.mnhn.fr,TRUE Global,Global Biodiversity Information Facility,GBIF,https://gbif.org,TRUE Guatemala,Sistema Nacional de Información sobre Diversidad Biológica de Guatemala,SNIBgt,https://snib.conap.gob.gt,TRUE From 48c22ee32af906953d61e4b57c9da9ade3856598 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Mon, 3 Feb 2025 16:03:34 +1100 Subject: [PATCH 15/27] Minor edits to reproducible downloads vignette (#261) --- vignettes/download-data-reproducibly.Rmd | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/vignettes/download-data-reproducibly.Rmd b/vignettes/download-data-reproducibly.Rmd index 05335bf3..8dc2dae4 100644 --- a/vignettes/download-data-reproducibly.Rmd +++ b/vignettes/download-data-reproducibly.Rmd @@ -17,8 +17,8 @@ editor_options: Reproducible data downloads are important for a project's long-term use and longevity. Below we briefly discuss why galah queries aren't -reproducible by default, how to make a data download reproducible and -how to use this method download the same data again. +reproducible by default, how to make a data download reproducible, and +how to download the same data again in future. ## The trade-off of new data @@ -28,19 +28,19 @@ up-to-date data available (yay!). Living Atlases, however, are constantly ingesting more data. These new data may be from within the last week, the last year, or the last -hundred years, depending on the source. As a result, even if we run the same query, the data we -download today may return a different result from the data we downloaded -yesterday! Frequent data ingestion means that although galah is useful -because it downloads the latest data, galah won't necessarily return the -same data every time we run the same query. How can we preserve the -result returned by a query if the query always changes? +hundred years, depending on the source. As a result, even if we run the +same query, the data we download today may return a different result from +the data we downloaded yesterday! Frequent data ingestion means that +although galah is useful because it downloads the latest data, galah +won't necessarily return the same data every time we run the same query. +How can we preserve the result returned by a query if the query always +changes? ## Generate a data DOI To make data downloads reproducible, galah allows us to mint a unique *DOI* (Digital Object Identifier) for a specific query and its -subsequent result (NOTE: This functionality is currently supported only -for queries to the Atlas of Living Australia). +subsequent result. A DOI works very similarly to a url, in that it holds content that is accessed using a specific link. However, a url can "break" if the url is @@ -53,6 +53,10 @@ In galah, you can also add a DOI to your download query. You just need to add `mint_doi = TRUE` to `atlas_occurrences()`. A unique DOI will be assigned to the resulting object once the query is run. +Please note that this functionality is currently supported only for +queries to the Atlas of Living Australia. GBIF provides a DOI with every +download, but at the time of writing, they don't provide an API to use +that DOI to download the dataset a second time. ```{r} From 0321cc835ada28a8fe9368dbeaabd72ffc9f96d4 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Mon, 3 Feb 2025 16:28:32 +1100 Subject: [PATCH 16/27] Support `group_by()` for occurrences (#258, #195) This basically generalises `atlas_species()` to support any facet, but only if you use `dplyr` syntax. This was deemed to make more sense than adding a `rank` argument to `atlas_species()` --- R/build_query_set.R | 8 ++++++- R/collapse_species.R | 10 ++++++++- R/collect_species.R | 30 ++++++++++++++++++++----- R/galah_group_by.R | 22 +++++++++++++++--- R/url_lookup.R | 1 - man/group_by.data_request.Rd | 22 +++++++++++++++--- tests/testthat/test-atlas_occurrences.R | 22 ++++++++++++++++++ tests/testthat/test-galah_group_by.R | 4 +--- 8 files changed, 102 insertions(+), 17 deletions(-) diff --git a/R/build_query_set.R b/R/build_query_set.R index 307f1ce1..6ff20c79 100644 --- a/R/build_query_set.R +++ b/R/build_query_set.R @@ -89,7 +89,13 @@ build_query_set_data <- function(x, mint_doi, ...){ # handle query result[[(length(result) + 1)]] <- switch( x$type, - "occurrences" = collapse_occurrences(x), + "occurrences" = { + if(is.null(x$group_by)){ + collapse_occurrences(x) + }else{ + collapse_species(x) + } + }, "occurrences-count" = collapse_occurrences_count(x), "occurrences-doi" = collapse_occurrences_doi(x), "species" = collapse_species(x), diff --git a/R/collapse_species.R b/R/collapse_species.R index 070a87e6..a0bf0307 100644 --- a/R/collapse_species.R +++ b/R/collapse_species.R @@ -20,6 +20,13 @@ collapse_species_atlas <- function(.query){ if(is.null(.query$select)){ .query$select <- galah_select(group = "taxonomy") } + + # determine whether to use `group_by` or `species_facets()` + if(is.null(.query$group_by)){ + .query$group_by <- tibble(name = species_facets(), + type = "field") + } + # build a query query <- c( build_query(.query$identify, @@ -30,7 +37,7 @@ collapse_species_atlas <- function(.query){ sourceTypeId = 2004, reasonTypeId = pour("user", "download_reason_id"), email = pour("user", "email"), - facets = species_facets(), + facets = .query$group_by$name, parse_select_species(.query$select) ) # build url @@ -43,6 +50,7 @@ collapse_species_atlas <- function(.query){ url = url_build(url), headers = build_headers(), filter = .query$filter, + group_by = .query$group_by, download = TRUE) class(result) <- "query" result diff --git a/R/collect_species.R b/R/collect_species.R index e8624496..92e3327e 100644 --- a/R/collect_species.R +++ b/R/collect_species.R @@ -7,11 +7,31 @@ collect_species <- function(.query, file = NULL){ }else{ .query$file <- check_download_filename(file, ext = "csv") query_API(.query) - result <- read_csv(.query$file, col_types = cols()) # NOTE: used to have tryCatch() - if(nrow(result) >= 0){ - names(result) <- names(result) |> - rename_columns(type = "checklist") + read_csv(.query$file, + col_names = get_clean_colnames(.query$file, + facet = .query$group_by$name), + col_types = cols(), + skip = 1) + } +} + +#' Internal function to get column names cleanly +#' @keywords Internal +#' @noRd +get_clean_colnames <- function(file, facet){ + column_names <- scan(file, + what = character(), + sep = ",", + nlines = 1L, + quiet = TRUE) + if(length(column_names) > 0){ + column_names <- camel_to_snake_case(column_names) + if(grepl("ID$", facet)){ + column_names[1] <- "taxon_concept_id" } - result + column_names[column_names %in% c("counts", "number_of_records")] <- "count" + column_names + }else{ + TRUE } } \ No newline at end of file diff --git a/R/galah_group_by.R b/R/galah_group_by.R index 75f5ffae..eed65e45 100644 --- a/R/galah_group_by.R +++ b/R/galah_group_by.R @@ -1,18 +1,34 @@ #' Group by one or more variables #' #' Most data operations are done on groups defined by variables. `group_by()` -#' takes a query and adds a grouping variable that can be used in combination -#' with \code{\link[=count.data_request]{count()}} to give information on number -#' of occurrences per level of that variable. +#' takes a field name (unquoted) and performs a grouping operation. The default +#' behaviour is to use it in combination with +#' \code{\link[=count.data_request]{count()}} to give information on number +#' of occurrences per level of that field. Alternatively, you can use it +#' without count to get a download of occurrences grouped by that variable. This +#' is particularly useful when used with a taxonomic `ID` field (`speciesID`, +#' `genusID` etc.) as it allows further information to be appended to the result. +#' This is how [atlas_species()] works, for example. See +#' \code{\link[=select.data_request]{select()}} for details. #' @param .data An object of class `data_request` #' @param ... Zero or more individual column names to include #' @return If any arguments are provided, returns a `data.frame` with #' columns `name` and `type`, as per [select.data_request()]. #' @examples \dontrun{ +#' # default usage is for grouping counts #' galah_call() |> #' group_by(basisOfRecord) |> #' counts() |> #' collect() +#' +#' # Alternatively, we can use this with an occurrence search +#' galah_call() |> +#' filter(year == 2024, +#' genus = "Crinia") |> +#' group_by(speciesID) |> +#' collect() +#' # note that this example is equivalent to `atlas_species()`; +#' # but using `group_by()` is more flexible. #' } #' @export group_by.data_request <- function(.data, ...){ diff --git a/R/url_lookup.R b/R/url_lookup.R index 22f0388d..f7681dc2 100644 --- a/R/url_lookup.R +++ b/R/url_lookup.R @@ -18,7 +18,6 @@ #' @importFrom utils URLencode #' @noRd #' @keywords internal - url_lookup <- function(type, ..., quiet = FALSE, diff --git a/man/group_by.data_request.Rd b/man/group_by.data_request.Rd index 1a1edc05..84e44446 100644 --- a/man/group_by.data_request.Rd +++ b/man/group_by.data_request.Rd @@ -20,15 +20,31 @@ columns \code{name} and \code{type}, as per \code{\link[=select.data_request]{se } \description{ Most data operations are done on groups defined by variables. \code{group_by()} -takes a query and adds a grouping variable that can be used in combination -with \code{\link[=count.data_request]{count()}} to give information on number -of occurrences per level of that variable. +takes a field name (unquoted) and performs a grouping operation. The default +behaviour is to use it in combination with +\code{\link[=count.data_request]{count()}} to give information on number +of occurrences per level of that field. Alternatively, you can use it +without count to get a download of occurrences grouped by that variable. This +is particularly useful when used with a taxonomic \code{ID} field (\code{speciesID}, +\code{genusID} etc.) as it allows further information to be appended to the result. +This is how \code{\link[=atlas_species]{atlas_species()}} works, for example. See +\code{\link[=select.data_request]{select()}} for details. } \examples{ \dontrun{ +# default usage is for grouping counts galah_call() |> group_by(basisOfRecord) |> counts() |> collect() + +# Alternatively, we can use this with an occurrence search +galah_call() |> + filter(year == 2024, + genus = "Crinia") |> + group_by(speciesID) |> + collect() +# note that this example is equivalent to `atlas_species()`; +# but using `group_by()` is more flexible. } } diff --git a/tests/testthat/test-atlas_occurrences.R b/tests/testthat/test-atlas_occurrences.R index c49ba640..ada8b686 100644 --- a/tests/testthat/test-atlas_occurrences.R +++ b/tests/testthat/test-atlas_occurrences.R @@ -211,6 +211,28 @@ test_that("`atlas_occurrences()` places DOI in `attr()` correctly", { rm(cache_dir) }) +test_that("group_by works on occurrences", { + # compare group_by with atlas_species + x <- galah_call() |> + filter(year == 2024, + genus == "Crinia") |> + group_by(speciesID) |> + collect() + y <- galah_call() |> + filter(year == 2024, + genus == "Crinia") |> + atlas_species() + expect_equal(x, y) + # try with a different variable + z <- galah_call() |> + filter(year == 2024, + genus == "Crinia") |> + group_by(genusID) |> + collect() + expect_true(inherits(z, c("tbl_df", "tbl", "data.frame"))) + expect_equal(colnames(z)[1], "taxon_concept_id") +}) + test_that("atlas_occurrences() doesn't return secret information", { skip_if_offline() RUN <- FALSE diff --git a/tests/testthat/test-galah_group_by.R b/tests/testthat/test-galah_group_by.R index 766f0c50..0c9edc89 100644 --- a/tests/testthat/test-galah_group_by.R +++ b/tests/testthat/test-galah_group_by.R @@ -17,7 +17,6 @@ test_that("`group_by` fields are checked during `collapse()`", { galah_config(run_checks = FALSE) }) -## FIXME: results of single group_by are not a tibble test_that("grouped atlas_counts returns expected output", { skip_if_offline() counts <- galah_call() |> @@ -28,7 +27,6 @@ test_that("grouped atlas_counts returns expected output", { expect_equal(names(counts), c("basisOfRecord", "count")) }) -## FIXME: results of single group_by are not a tibble test_that("grouped atlas_counts returns expected output when limit != NULL", { skip_if_offline() counts <- galah_call() |> @@ -111,4 +109,4 @@ test_that("group_by fails for four groups", { filter(year >= 2020) |> group_by(year, month, basisOfRecord, stateProvince) |> expect_error() -}) +}) \ No newline at end of file From a47d441a0711e373e29368391b357128726f06aa Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Mon, 3 Feb 2025 16:45:26 +1100 Subject: [PATCH 17/27] Update NEWS.md ready for release --- NEWS.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/NEWS.md b/NEWS.md index 81614574..3f0d0d7e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,16 @@ +# galah 2.1.1 + +### Minor improvements +* Support `group_by()` in occurrence queries to allow facet downloads by any variable (#195, #258) + +### Bug fixes +* Improved documentation to use `galah_filter()` to specify a `taxon_concept_id` rather than `galah_identify()` (#245) +* Adding a `field` without data breaks occurrence downloads (#248) +* Queries that filter using both `!` and `%in%` parse correctly (#251) +* `show_all(lists)` no longer truncates results to first 500 rows (#252) +* `atlas_counts()` no longer errors when `group_by()` is set but record count = 0 (#254) +* Empty tibbles returned by `atlas_species()` no longer return different column names to queries that return a result (#255) + # galah 2.1.0 ### Image downloads From d5e2697585d5b3449f2479d31533412ffccd0483 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Tue, 4 Feb 2025 15:00:21 +1100 Subject: [PATCH 18/27] Fix up tests for release - add skip_on_ci() everywhere that previously only had skip_if_offline() - atlas_species() now works for UK - collectory APIs rebuilt for France --- R/collect_metadata.R | 25 +++++--- R/sysdata.rda | Bin 17525 -> 17522 bytes _pkgdown.yml | 2 + data-raw/node_config.csv | 8 +-- tests/testthat/test-atlas_counts.R | 32 +++++------ tests/testthat/test-atlas_distributions.R | 12 ++-- tests/testthat/test-atlas_media.R | 14 ++--- tests/testthat/test-atlas_occurrences.R | 19 +++--- tests/testthat/test-atlas_species.R | 16 +++--- tests/testthat/test-atlas_taxonomy.R | 6 +- tests/testthat/test-count_arrange_slice.R | 18 +++--- tests/testthat/test-galah_apply_profile.R | 14 ++--- tests/testthat/test-galah_call.R | 6 +- tests/testthat/test-galah_config.R | 6 +- tests/testthat/test-galah_filter.R | 24 ++++---- tests/testthat/test-galah_group_by.R | 20 +++---- tests/testthat/test-galah_identify.R | 10 ++-- tests/testthat/test-galah_select.R | 40 ++++++------- tests/testthat/test-international-Austria.R | 40 ++++++------- tests/testthat/test-international-Brazil.R | 34 +++++------ tests/testthat/test-international-France.R | 30 +++++----- tests/testthat/test-international-GBIF.R | 40 ++++++------- tests/testthat/test-international-Guatemala.R | 36 ++++++------ tests/testthat/test-international-Portugal.R | 32 +++++------ tests/testthat/test-international-Spain.R | 50 ++++++++-------- tests/testthat/test-international-Sweden.R | 54 +++++++++--------- tests/testthat/test-international-UK.R | 48 ++++++++-------- tests/testthat/test-masked-functions.R | 8 +-- tests/testthat/test-request_metadata_unnest.R | 12 ++-- tests/testthat/test-search_all.R | 32 +++++------ tests/testthat/test-search_taxa.R | 30 +++++----- tests/testthat/test-show_all.R | 2 +- tests/testthat/test-show_values.R | 24 ++++---- 33 files changed, 379 insertions(+), 365 deletions(-) diff --git a/R/collect_metadata.R b/R/collect_metadata.R index e33bfb27..c873fb81 100644 --- a/R/collect_metadata.R +++ b/R/collect_metadata.R @@ -63,6 +63,11 @@ collect_collections <- function(.query){ } result <- flat_lists_only(result) |> bind_rows() + }else if(pour("atlas", "region", .pkg = "galah") == "France"){ + result <- query_API(.query) |> + pluck("_embedded", "producers") |> + unlist() + result <- tibble(name = result) }else{ result <- query_API(.query) |> bind_rows() @@ -98,8 +103,8 @@ flat_lists_only <- function(x){ #' @noRd #' @keywords Internal collect_datasets <- function(.query){ + result <- query_API(.query) if(is_gbif()){ - result <- query_API(.query) if(any(names(result) == "results")){ # happens when `filter()` not specified # Note: This assumes only one API call; will need more potentially result <- pluck(result, "results") @@ -107,10 +112,13 @@ collect_datasets <- function(.query){ result <- result |> flat_lists_only() |> bind_rows() + }else if(pour("atlas", "region", .pkg = "galah") == "France"){ + result <- result |> + pluck("_embedded", "datasets") |> + bind_rows() }else{ - result <- query_API(.query) result <- result |> - bind_rows() |> + bind_rows() |> relocate("uid") |> rename("id" = "uid") } @@ -172,7 +180,7 @@ collect_fields <- function(.query){ bind_rows() # if there is a 'stored' field, use it to filter results if(any(colnames(result) == "stored")){ - result <- result |> dplyr::filter(stored == TRUE) + result <- result |> dplyr::filter(.data$stored == TRUE) } # now mutate to required format result <- result |> @@ -273,8 +281,8 @@ collect_profiles <- function(.query){ #' @noRd #' @keywords Internal collect_providers <- function(.query){ + result <- query_API(.query) if(is_gbif()){ - result <- query_API(.query) if(any(names(result) == "results")){ # happens when `filter()` not specified # Note: This assumes only one API call; will need more potentially result <- pluck(result, "results") @@ -282,8 +290,12 @@ collect_providers <- function(.query){ result <- result |> flat_lists_only() |> bind_rows() + }else if(pour("atlas", "region", .pkg = "galah") == "France"){ + result <- tibble(name = { + pluck(result, "_embedded", "providers") |> + unlist() + }) }else{ - result <- query_API(.query) result <- result |> bind_rows() if(nrow(result) > 0){ # exception added because this API isn't always populated (e.g. France) @@ -297,7 +309,6 @@ collect_providers <- function(.query){ result } - #' Internal function to `collect()` APIs #' @noRd #' @keywords Internal diff --git a/R/sysdata.rda b/R/sysdata.rda index 93694ccc7ebb9e6539e524e6dd55f94aab9191ea..3084051620559f7d881524c2727bb926c4918e33 100644 GIT binary patch literal 17522 zcmV)LK)Js{T4*^jL0KkKS!`E>Z2(mZfB*mg|NsC0|NsC0|NsC0|Nc)xK>$SIUI-Wf z01`j}Nyp$;KYaJh4|yM2_Xkg)0002r3wOS7DnJ0&&<{WWeckidn7n;dxxSiC*mbMD z_H0Pgu7=#^g?hprG*4f4>jTY+3#=3a-S@}NSjDse*xPpQSgbbFKFr_|R&{s-T$ZPM zuWH_jVbQv`RnMS#RKCm3i@05;BBJMP?7`W!lXJLdxbEKWhXPcqO9`n{87c-{W{tp| z_1r1qSo-;}GWSf*kCE4VyQ^&s&F<25wY|-|y8GjuUXYX`0%*_^38tE235liv zMk%z>qd?IxCQVcFF)8WjlfrtY)Xf^6rpTIpric$yO$;Dupddm45upIo$Z4WY4^wF# zQ$gwg000000B9bN82|tP1589jnwoh@pwl%p4^vI3*-bqVXwYe(00000000^Q00000 zNu-EG2q2hKVoZ~IQR)NK{ZXU!O*Hi$q3UQJpa1{>06jne00000i6R67O`w$aRQ6L; z$kW9%@{iPLnLSTL^arV_;+PQiFe5cIVLd|#XwbwoGypUW1W6#80%QP~jSNuGC!rap zMuAiEYI>*Yjp)@qPfb7vq&+|Y8UQo^00E`{9{?U7`wnJuUat1`(M*)h9boe04g#BT=rUF4#m;1{#SVz7Wff8wPza!NMNq;3K#ghuk>{j6 z4^ap%5Y9rC)lbC3F-8djK^`yiKLs=JTb;PqcJ*UzO4b?~q|Q4y8%{1{{?@CPvU10k z_1c|lmn5j#JJU=ry_7pQhV>8*sEYMKSC))dWDZ{2)q9urr9uyxQw>($9}7w9mba325(szxIB4pxD8yM!qa$iRssCq&bYdme0%4Ah92 z51|nQdHix~NiGHODu37VrsMh#wfp_E*$F)sX_d;V8Te0=?zUB@`x`IH z`*Fv!7upE}O9E7!!<+B>)ktZfD&@AcwFHG`RxdarWkFR<+<3SWm%@yB*Ni1BBwQ|)NAfZD!-XdZvedh6bo7Gk4USFHO{?qa&!AhRd*}Xsq zk7N1&nn-Q{E=20CdI+kZD!Hyw`L2MUYvHeR8Jg9nC0pg~*M8Q6e{8Md{^)@*h7TUxNF3y_+5=d10a2l=!ie-<>meUAR-$GzD7cE8;l4> zY!>t74R*VCF-Hv3clP&M?)Dhy(UuqKb9f=PeHr%I-L8)pWhw#d!AG-RKuj+JJHFRl zgilBL1Fw3?$k3F)j7`oR{NZ^KYqFn+y4ej8z#PD(-hhyFV*}&!+m(ultuuxY+_vv|$qmU^ zdxA@A*ECi!0iPPp6S_d-}(2*vA&Y-T~jxdSW{n*6%>_=pB;8&9Vd z$G$i&LN8I_#dy7H2D_D`m3Zs__53HWG0Gq>fdqDCu?%w z&~;(by!mIC^-qra-^RUJ49{uX$$G`>th5NFX!F8^lI9&* z{ObN>i3YxA{0nVfiOXDi{iZz47uTPMe^ zIA{IMnwFB{VesA;K zX}*7#amab&dtY6CEjou*>F}?6#xqM9Q@#pN3?XT!< zYZ(IVAf`$NbnoMrD7xOfD2@gxSzYH)&(EgMKZdUJZoSQ>a%T&GcPt2_w!b3sIepWM zC*`g?Ikx9u+@S4aQN++|G|Hb?)&=B74<&13R0VMSrxVWhgVqCFnr zLX4~W9KSoqsJFXzs`^<5?4Xc#yA6l~;VG|08C^zDk<%8?@xUrV8Rt-dd7uaiU?7T& zKoA3{fI)%^G$f1|Ai@Iy0Y&hE1u6o!fQqbuf}naV1XKwTR3rpsRzVpMK|bt6>WHYV z)G-Lk5mY(&4DZ(aKE=lwxt(wWeli9|)5lm$XI%+@tVA;?{Ar+X_sH|}7fU?=a3GIU z(W+`cc$2{x``5w+ED>iS8pJ%A+Ba6IVX3)z!w#!n^zWWI`uooPu=eP{i-JBBD1r%D zhEX(_VO3NVQZ~0Y*9FLIIHMbW)}u5diBSU2)uuAP_OPxY0 zlGl(XfGt;Lr}Y#aE`Lg43PuBSz$m+wW&2E>@+?P8eyfiCE}Kq+&*1k89gSFfz@(@Y z-gVqO7PaI16K_E6BKP<19Y`{^oPbYz>#B;EUFQA zAP5Q-h13bjxb8(7k=Jk|82dW&HY4(IvVyVDCn0Z6Li_QKqmqtrleRpCmFT8aKufrQ zEJzi&*IV+>B~|1xkv44D67LvHT%!r3Ku|3SVFCaVpaK3_*nw`#YS8jvD1nfIBGt2o zDcnRv3nL~1x;W_)i3?34@3&tm2)85NToPNy!5|oQn1O?#;G-QcLo9LJlsaKpHYmekT*fXr)L~2ly;~$u zxe7ZZb+Cnt-I+;DqU<+FY2lYH5gT7dzQ?{$Z@dY2a6#w@UDPe;GSAXMDH{bK(L*Q3 z(;pv_S3bHel>^1M>}3!$?D}#yv>nAEj0|TPzEwhs0%DTadtAxGs22n&xPYLg%FvLh zRhmfra9bf|P^yIn;kE8^G4knBN(+S>;k1{OkWF_s9BayVl~4@w8V$vU!raEjh!g?f z6zQ$R4obVJ8em1s=nWK6R^MTY^DUdgGKltbIvKSZ1Th%V*qxmcqIOW~6nx4VG2(`< zI7J~*VBQcZiks`qWRwuXl*5&vhB?JzjvY`fA!Ppd$3fppGV~tJB)ucE>)1^j`nnl0Ys4I)Ts#jmKd-BmFka={}u8@2Oc$cbeGE%@BmtL}JXlTR}5Kk*X zX_367H>;LAvnJ^bXtMVf(iHiHQIhXvu|Xc(gww|Yp>CODGnPk_hN#677%3#|l4UYy zR!ZY5j#9~vBP{uBh=*8~dA4cpzQDOu*r`#&OFm2x*s9#AC%{m;dO<_XHL|{p7RX$U zJ6=^tY>%$l;6VDhaYBr<%Pdr^6v>L!M}dVA5h6l0$TF|Mwo5LhV33sqpM15vMgY4) z8%>B_tr(M}k4A>ziBLJY%fRO#21VjjGmKb_prHazTp`HfU>~DCa)q~dUd62t?yhZ?f$&8+#hiUuhBmI{mLuIa3e8_NvykDfRUZZs-BHf(_hE`hZi2pJ z%T>nZw8xYuFC^}Q!2Tb1#uZ^g#X@tB2fPENLvjxeqjPmgu(_hHfF6*Cq5+U~b%60# ztQ`YWtkh>0Tv`WZ4WfA96d|w!au*GZ0~n?mwBK3|%L8Cw?WAr4v`kzMvKzWwZg@PQ z79AdB6XL!8kb&tvzFu8hZMvC3@qy!Z>5c|Kzz8Mo9kbc73%NTi27+A--8xePU=-O8 z39rfCJU$J4DPwImao}q~5hM~WXk>&F12*ZQxG)Q4997WQEvfQDgDx<8Jt7kQ*eQ0nS25{Qbyu{fJy!BEj%Is{ZIu7^<5YNOSZ$mej0W)%F$I+ z%~KmOk&_UvI-yY0c6g@=Y@?3`sVPD+A#O?yBAi(uuM=bC)P`WO_exok=(y$-?(52z zMv4GP=_NKG9mgnIv|pKym16UQxj(7vtr#h370@(}3Xnh_j~tTa2OJ6T(Z@EK-BWwSaas2|ECuQMTi4mETPkiMUacHX#HQ z0)!}#mQ?CyW@V(CNjOPLMCB254u%&lPK(gOVGczHV!0uWDw0Sgl;NT2h^oC(#Dyky8GA6(!Ygo3MT$-9QOF1rz?z`>Mw<2cY z)}3e#GA^~O(kioB)*1q(w#k*gB>`w!^t*)b7z^dZ>bHEM6+ETINFNO$e~5!ngeP%9 z`kY`^w)8GLXfCBVvQ+(DLJ;j2{CxE12Q_c|H=on}QpAK}p{@G2VB7X@e=Cxvkelos z{}(s#WzL2$_k6>R$U)e9#LL$oOAo%NVtw(??4QDXh1aicB`3xGbvuL1ew6yWU+Ks+ zdle0*Io+at_6_TnTr@$5kPUV?MxoMey)RRzwYOs&G-L4f8DRyCqOb%w{V1!mzg$0RAZ8EH(}4YFzq`uKk8Ke(Q1Yb>#Rd~@x4%Yho{RMJs$Lu$srjMW zL$!<4dF%W_9p7-;PkEut>jOW39Bf+F*WSbC+s8s%kPXZO;ez?pEan6tV(@T@5TPf$ zPayI(3C>{1lYf!UGml|lga|X{(;kI`3Gvb;C=nlVVyJ|uP%awA!1PW~dhcG40_k+# zL>}NMCSp)%L&nGKHkyD$-s)xo96md=lK&=(lfpIP`uNAhckr2eaeKS)>zW&v+vg;| zz?1t~_+OvY@Z|7YEQcDs~;Cb zUrV1R-?ZAHu%IHUAS!~2Aa1h?DFI|yw$Q0j7>L9l(VsyK1o{^=L^hNm-|+&hg2*Hi z1~H;jI^+UJA{;aWd~j$$FCU9w&#P|1!ejtqP*sS9Bs|dO`U?g}=h-QhaGQ&12-9-z zn+@4kiC|YKHO#GdpL6zNI64F!Tr>z|Rn6Le$z8JdAn;pB@x67p>%1(T9hym4*Un6t z&ZQWfW*|cOr4vHP9Yia7pGLl6$A1-#=g+1vHvrq%(%L{Q^UQ zSOu}X3N8=H7kF^MasDzKOhd_RXqwB|*bu(S`Sdm~a>lKMNE}{pFxF>*Eky&*7PDx) z1_<#+5Y57ME}~{E)maQ3j@7JphbwZVW?(6}C^5t^!is7FH<`q)r0*N9$hiYJa%og8An{EfR9%DnM2_XFnZ+vHz_x#^i&kPxz7hzRYr#Q^h zxi`RGZiMKGpUJsw*|Q*-0(Y37Er9A5QtrW6Yo`dn`&%9Mf^M;&k9um^hG9&^GB{0w zxSoTas03svMg#{Tuxl8#3tcFMs#P7OpK~JP_>!)Ux0B@U^3e{xAv^Y4l41=&2b{yB zxTphDYy)Tx8JI%yEEvPd`Fr_d9_N3jmke=Eq?<@}lj-gG4eJGtai?acbYN`Q>6=3X zf{1i6HLFbDPbY-3utR1hjDk2wVA>?H(Q*1Ym=tH@48%q*96BxD2{0g_1dO9S&KxZU zWFt|OAQe@*7R{sFF$shVkf8Xo8%=vz3We`g6Jv~TSSdilvO-{ zBV#WGtxInLunKyXf}Ns((eH80V&&^y(Ssg-muiY<;Zhh_ za*H6C4#1HR&`0(GHe73F^gh{~da}KXZy@bb!tuMRYrP+r8o=D^rFLrG|t*<=) z(>);xx6IZRlu<+@qXuwD#ucDI)OI!5$abwl<~4b&?R!6#$-bXgr0(@?^udSN26cRq z0wDJ%vJioo5yCkLSrH0#mX)3D?mclU5tm77_R2=7NQ8{0L;|RzBuXS==6p5(_m8Vy zN+g(&$gvL$oV-P@RImWO2pf3?6%gN|=LW{rT2*T2NZ9qH%8Qp$k$ydV9otaxdNwJ^ z(%sE7N4i&N_&s3*_@08G;CjhI+LA6};lwN_&Ooy+D4;!GxUCnth0H&K1`Jd37a2qG zbe8fT2@h`urW&9m6A2uHM1mzJK@|-SIY{a&rEtyjOA-(hiU zUc+n4+$F{^_y@h^F~T+(InX80$Y~uF8a$-Ec-h5fDz)ppgU-889rX4ZshuW@mt) zJwyT+x`g_}3&!O=%+1Cin|+-Eg%SlL7|+P|m;No=x90xe1~GhG@livtAJyFpL>;wA zR7#zX6Xko}_wjI^kvC&7jX!M zMb_pioCw}D;}uO)Zh@<`yn1-)twj5<_Mw@f?oDAv4zP=2K8a&^Fbz~<27-BqHqwj; zM&ky-v8xY|CXHrGiGY#&JjtNk(Q&9^Wvs_Gp>Ka7`MCUF%A=k@^(Iffy6y9(bh;O> z6cN~(WqsBV@^MAretz{=S6))I%sgJoxm8X=lObJsU{+#lRiF&w0#6sic)np#m{nF3 zGYquS%T$HP20a9E2@k6F`t#RN@R1xj^rzBo9V7^YQt*c%?JIHxeAF0ata#4OF34Pa zd4Sa0)z?B@p2}g$=q?>5A%t5OA$cK)TEsVvAEVm*e)mpsEjkS<h^wbJ*jg0**|N^1Qk>s0w{6Qu|I^}J26Go_Lh53d+3u943T1Tb+9%J3=&Mp zXrhw!b6GdkReGZ<1};sDL;8a6(&9WDr3)d^@h%2ng5QClFbgHoN>ik=QxwI}Rq;}b=%_q&$wXL46j6o@qbqmg#?XFN zHZ@#Wu|snP3>)-}nh{Z><4yuCYMG1GT?P2nm*!K2VOR57C9@N4sBkKKgC7R*$<6n@ z%QAC1{hz1FL)q^4yPJo(xz0bYkNEETeeb8@sl8)=@$f^f^ZK+pe|@g@f&4#x3O#>K z`@4C+!Be(*+X||xr-Wt=W#1I?`(1e%o_-d3z0h#M*i%4FFwZqJ16lllasv>lo8C^w ziWT^tB6oZ+0(T~p@aemh%oho&=?GwKELb4}_z_Az|45f|crElWK?5m_ z++8$4ac{M8(4vZpPv}}%D2N&$SMTtmI}SQHOK}W7EcuePCQ+>N7`tbO)uKsSTh45@ z0~dKjVbA1bb-gEUzfH`FEn!9rVT_JEq5z&oNED`N=KS8OefNB8(x~0uZ^0BWKyXqi z^SMVU5DfjaQ|K8*6j1yQV$a#6VB9hYgy%UJRYi?cQ!gg3#Hy-jO}u_`jWH`gE&dKeo6x{OtQujb7Nd1>HkF`E zFgq9>YBGtTF!lKSIqB0o1p?tx>=;401DeDFr8EMps8KG;QoHlaY!c%jJ9F2MB*Plm z=W!^p>jrNwh9(tMM5t~!!gcoMd|hTF4)))-%g3;=Oa_9`M#8`?;I4SKA+Tb64#{gP zp)1BIH9^nY(TogYKSD0T0rs7=^bjy0beGx=$Sa8zAiuXI3u9Fw>Nqz6Fgt&4;|=VJ z%n~|bKtReF=%8SQk&zg4pG3mWxHfe;fP@%N2oVBS5CKR*&M+SwFbQ%O?{V^btG;mH zX^@IwY8{8k>9O)d>%*ClP~XgeaUJg!c$d`ZJpS&fdyk#Fm9ljnss}#}kg{{40mxy# z=aT3{KS*|D;YV{;xwE>JElIhxZ8%|jLJ4-^fY}U4j%Gf<=Qcx5Gg1sTuuFa$1{=-@ zFo^l<+3Ov8+j+FX&va4*r!RR;DZg8jSXybH>g zA1*sd+YE+n>llH`e`ArbKXi_YMQP7#hGTlsg1pF<|yiQlIAP4*?{R4d^kye0)Y5N(=-J zGf~ICr3}s~pyuukZo2`#A&Kv%vD&?&1BF@896`83lBWFCtiG!Oc1H>`YNYJ2m=xN< zA8O!D1Q5k@J>N`1^g`|{T_g#>;E8f#ug~>5Ab>pL7P&5n#4DTid4FDbFgvpcJOYZ< zsv>SZo;zj^Fv3&`a{&lnPM>h36;^oNtO^T3NKOeM^+ADvABn5=+imS?^^+LuMj4p} zB84kxmaL|zTxEzdYRgU1n$TU%V3{zhKF^-1$ci2b!MD)BtH{z{3NG)Ub>es79eV3Z zX_HoRn+Yn8oNF?gmhez8L$<8P zY#H8ST0{p%QzQZhye#QLJB5(HZ3Q@>qf0s(2>{#$NI;j@d@;`&oNy_(>U$*(1THVt z!MNwQBfdxmI*1xoG5|S{di|u^uTMnur(ysd+2$V!_Rk=bF+>a|2@zF_#tOs|C@hLY z)#@1Ucpmpd(E{#+j03&_Ui{dFh5c_ul=N{-;5i6-Ofqcb1M5{MhYB625yW2}-bnVS zG#Ae32UvuPo4bCe()U|7haGZvglszq-sP=Kf)Nh-h9~c>i}87k7K@NCzu5>x4K)`o zcIWlp4d!Dei(p#7L!%VPF6>J{iu>5NsJsKnJP;!+osmNVY!EtZSxY2e9+$UV4G(AL zGDDDlRtG+oQ4}H|A}A>s>)3k%c406uU~k(H6a-LYfT|2+0MB#k5Iyu9?S=@dh%rzT z4%Ze*p@b|!OCN0v28rHzoS?Y(pav z#CM8h1q{^#VBuO>3knJNfZGz#Knm$ zRmovH4*tPMut=k~X^8p)LLmkr>&+}5*%kh+tu=A<<8|{;z70_CRYZ^~1q>7q8AS?M z2uOrWOFcVM_5dMdp2g+QT@3<{-(oqJA_upfT)$g%;m~n-b*hUtG*^;xIB~3ID;TQ` zO4O=2(H>iQWR`G!Jej1twlR35g>ZNrVzZGqlTM;==>*}hoCu^8F%8BBB+GTh36l-X zBwHAm7gT6$H#Nr_l+4H+Y`PF_4lpR7axxrD-WUqO_X1o_R@Mi}-hFKOy)!)B|t=c~Jg{ohW#Vl{(k=$WV`alcPqUG+ogw z1*+28#&wutUvf8!9y@d$wZdK}lGL0%Vb#qh>ZTE@N(m%mD)y`bhrl3ua z`%6QP?D+ezMu})xD5cNVV0J{!>Q|7~?E&izEC?|oL4txH2LCz7SVP>tQ-qJhhjph#F?r6F0xj1dBC&rcV23?f-p!WccWuJ@Q~c`IOEg*bJj2+Zc8Rxe=5 zRH5w7>x%`h@h~Ruj4Gf~kV04zmb}D?xN^UgGOW5aV7i$X2>_tNA`Juh6J|kc3JZ|0 zdJ@*kTV!cOt4*v#Qi>D^bYY5Nn2VuEI7_Ow&zNWxO zLEQ~dH59p(-SYy%M#CzVWK>87!j&>m1DOj)Q=nyEG1hE47eY>m>Bf05@`19OWnobb zK3V0mo?bbIZouPKK4wofLCtV|J+cJsCj}LAtuEFLVY(D(1YIx{P*7edVhAYdWXWa< z2x9a)E|Yr3ut>2O7X_051a=M=9e8)^JhJ1$U!%n6v%|Y1!SA17p*tROs+X|R4Wb%A z3P9d&XEI*q?8&*y0HEK2o~!N2UGatv!3qVfl+56MGjk?E-I%hUae!nt=Sb;|P-d3g zXjaThcDESX_*G+UHx+&-CXL^Y^q6KX&p4?e@jZbF5=L)Hk4#6n}fCM>4CrkqoDljMHG!jjQ%>gR_ zJ5y%v{H#398y`OTg1w8HGeS7vLEi19*m3~u6y+fRgCWarJ?0QBWnC#9$sX(oK_mhM zl9O6o_vOw6j$d(P%^*TZF$US5Y_8z!EzGY~4ej!+RK+Os$3_$@<4f=>4p&*1K+*NuvP=qikU7?x&(VB;wn=u${(2P9Y z;Gv+vvjg8f{Y`s2^9<46ogiogX^-R z_~4<_m_fLl_ufgihZUt@4Upuf2y)n3aI98&D}t`-h;${!mknYY%f!l_PQzQq95TdT z{U>zy_HPh8M?d%6$rHTp!-CSAa0Y|iMalqBAaqnn_bN#O3chA1st6~noNAZRkggq! znH5w4HnL0!2wq-mhFfLAl3fmr=<5fh?7ThvZE%I;WIXRa%>)hLK-);R(ZB6*)vDIC zmbI;BNjogFS~-(!G{Fe8N@3nwyKS~hZJBFHCSO_GuTLidp4ySva%pN+A?Nn(Eza7gjNI*|6xEqY!SLQu4V2B9l`p=yi;{buRh)XN_Fi`U~>ViJ=Kd`5d5*EuNE zm<>|Fcz$kz+jE*;FCFp&d6bHS%NaemV*v>THU-dVWMHqops93;XdXWgei?vV>pst= zzgn*VI=3oFgW}LY!~zjMH1-x2LXj-MrVZS`eEs2YaH|ib;_0cg8anvxZi~8@;cgZZ zSnxPI>~|fP+mF6GyOEJp=ZKaP;HZgs2nvGO>h6O5`>g|jq<}CoK;SDBg)P!O@C+b^ z1=8$T5il?^&K0S@d%aLws0=j{GJ#8xX#hD0XxAr3KzYyT?lOZ6!k;|?qB0Dl8c;?g zL=}gK%8X+eixvta5KCcT2*ePhqmt#hO$SUp5btTh^|=o-VlqNRl0afHWD+bLggRQTf()-ua|ny}&pQF$Cep)j6kI5-HCyJ#nCza{ozv=pG?VZ`1H+qN7KLgyu;L-Lf=vtdMh2;^0Ae~Vnz{$jWfL-(dIQo9N`26G5^(W7) z1ugADjc6G_kxuU&^1^z?i6ld{d+UhBf(R<37^ot$PwI#Sk{<`fBrG%`6pBQDVj+P( zBqxPRwv||l3`rSjC4zzyRGWqoA|7C9p!T;&X#1#S8Igd-0pBMgM^q>VQGkrxLYpa_ zHFDMYP^>7;5f`=_a94A5g9tM-mJ9N*4Qs*Ccsj)TVjnM9AFUn4jkKZsf&ZztN{Fhes}tD*dKxFZj(;$~ zgim$^i)nNRv-tkC@01>;bHPJl)o#?&piYVKaS16Q6ZCqW1EYv{%O1}otiQmN;sKmO zPH!%+sjc5Sx234al$1dbc54djN*2Ng*JWoa#QnoSY%@nVZ;m+U%bm`HXiVj@nh(dH z0>UCkB{Q8|5P)}d*B>FG9DISt73Mgl-qiRtp%w-WnD7uoq6M{~LN)fPasU{CK}sYy zLXMT~oXkQ%DpVAZgiu(dAhIY$Pzu6DkgQo!Nh(jVi7X@uMi5aJ3x%apm5U&p<^}`| zEZdzM2wvn4v^>{ehm)i5ZPBZg!H(cE!cqhv?65qW~@YFm*uc2U!R}AQfFRy?*c$n04kF5X4ic=i3YdLbBgu z5rt74D5Ne#xVJWtBN!7^c4Jb7{e{)+cVp~xWvHU6ESo13Hk(8sr-nNLmARrgN&?9c zC(WXaeb%I$*iaM>DM*dB2L!F6meTf;10XOFgawckP(mO0-d>!pT}@* z=PE;*f_LYe188DGFr(T>$nOD>o`a{;CltCDB0_*CK^hOqQiXa2 z%$K| zXo4y9JpPyJYu>#)X%!3*E^`)dejC&;OI`#-={=0B4lkC++04|JL*HZbXNq4QQOJmK zJ5D^75T6cY77)avAxmXsS+qnnFo#kGcQ0>`o5!M8j-16EfE6tWDkZJj^#tRAE1_Hi zG`kRY1VY4pNHmcx16~m`nGxyUU5`I99U^pz(1)ZYg1~r3+YADp$EZ%F$;cB!t?w@P zUoBZngq#;xg2G>PvV|L#Ik$DqbF%R?XpLm(K&~oJ&aIzx%8$xq*I9KTV-|>IT=t5V z6rw3dZj!di-#ua$96jeJqo1kHgW}XFLXu>OB?BP{nxGORoOPz+D;z;Wn8MZ)VUVy# z4=Xm|qYMos!Gu|(QYDuXL5wpf}tQtTNVl#>JBJunUaK|n7Q=j=&7oc_B!m~e{tSYry3P=XyY63Y;t=X&!y zi+INbfgxawXnzQBf_1a?9DH(mom~ba-|EY|&?kI0?g(!HZg)jTHyDR!myrXg2Lg60 z0>u&nkd+7)3lMS#h5Mvs_X;F~6Osg&A1&<#Ku%I?EHN}Qfud!DX@#8e$bd0OBMqDx zktR*eLj+?Wh(wd^9!;MRL6EXQ4v@|!2?9IaKFM}Sym@w3Yjx}cJYdQ~iv^KXge74J zq?61<2&qUEr4(dSW3^uh2GhZ~yd9zgyrWUd87?NJ94@zt8zmO2rVO+h0GA=YA&CDk zI924m7XfQ|*blw#Ty^!(h5&TB-n(vyF}y3><@J1XTEDJs5qO-C+(`rhkwuXXGNnq6 zBt`=jAT`Dw`l3V>P!yTWVHJ^7P!a#dgi~Vz#%PuWB$W{X9E#0mO>Y7$ z6a|wY8u`k4WGEY+Tq# zF^*_HN-wbQlic|e%fSFJHLAYoaGSy!cu?VX2Q^V4#2iov%jIyM$K)YoG%W>)7=SvT zs6E4{Xn=6;rM(86<=V+-@El@}p+;(OsdpGa#xk5sU@`Bw;3xKthnT=x3%; zUx%?2m}3z}DJk9aiSB#dK-teIA`BR?jEp3Ngpq-o$J&Q*cV73wz2D=$=da8Rg!SdY z(cL;IRTdz|3*f+{wRz?7x=0Zimw{L#uc)q5SfiWbe3y8;{y)0j{{kbO5Z0W>JKgo{A zvN(Z3x8a2Vp2Iw&Q8Ls+x2Qt!I1Qj`gd{klT`QyjT4r6`&}o zs4RUQIg{R|7f_Aoq79TAxZHZbYyusNwXI|Nks zCXY1(LcL)4sSbS2Q6zaQv(a4m@Gp@2&$B!FxI==%o5*BRkewUE0>zw zQEF8D`&43vHlu$Ql%FD20I)R6@K_>#Rt(Do!zC6A5+IS2Nh=_P5f?21isDNc<0}+V z1X(iBM$|z=f&6BNS{jUP zR$!wA+)nU)cXeRb!c&Yt(Fc zV>^FWh}&)+sjLz~LX1zV9CJCJEc*0htrSpj@h(t-jv$#J21tk$`QMO%P=WSRD2k8+ z84YTHZ%k6XORQMF=#*Qmfom8Q<~z6LjD7%4bJrK<+C$YVIA!#pR0x!OEEM59Cq0+I z*!)&gNC&oYoE$O|!=%{18+zt|8Z-WEA4!AFiB+fvM~A`p`#rgjTU^V&iSRlO2?8vH ziy#mn2&jUg6;?12gcd+P_skeXK0Y#M^|YTP@<7!*Kxqk1SrBWRcOMR5zRYpNR)FIy zTYYxDn4q#iBM3;4gDY|H@9<)9P|aXWq{_+QB3O)IY~6*-VJVcaIN8R_zrESFG~Xc{ z@_=p7BMu1M4Zz;EjWlF4AWIfRr8_7~utLj9T#_)0LIVPb8#4&Ih$Mm%A@3{rre9H~ zK94^3cV@s1bgh?EB%9}7aU_yPKy8i`NF)LfB9W8F*4v?H0T{9BH?T&7eS#d}vOs6}kz{K%R($e#?#Hu19 zAcj)bELrJTkWCxX#gt10U%s?O@c4C$G66(IHuES1g>Xk@!XrC3Ce5(|qFjRz$P}9n z_7EtNOmvvqvf|=SEQUi6$xigzc;0+@~)rrtu#1(Bhlv(ET=s&C4b!FSSR)nI`hj!Kvl1 zS-HmP%ipxU_g%=!UM#O%afXfwth2{fX0cu!L`*aVIK@V)4jd&!JwDRnVVS7_V97Uk zgh&cOVF9Z>dc5n4JWNzgTT+bE4t!HsUHWH~_V-ICvEo5quHYX|5QxWi_ z?;s~b)7~{nhwGqZHd1$-96_^$S&4{+DP)Af2m@CcB9wy|2q>rw5fK=Yj0z}3T3Bir zR`C@n-IH8oHt*GWiHl=0VZy2KPAZI&{K+xWE8nu}SYBy1~5Q~tP#50M6sK}V1mpDX`y8;51 zFqob!zA+=n6a#Vrt_8Rc2n4JoFc9Nn+QThKlm$2Qx_zV1E>|%mSZ#p^mh;>5ns3$5 z2uBTjS32s6@lOTFYlJOB__JlvUk&3>cK9uvxHp{PeNc04lzd+K>B7MfRx21uKH+3g z0E+=+l}<`Pvp_deBry1e6^ukdDWH)|2$FoL857+t3lc+{n+lpS6eXz{m{BHoNmK2T z#PBwA;lhc-hBhIeo6moWS%3eD$|N=J9)vhp@oxuX%$t< zR1K>)VYt$Gh|(1u>eSC3*|KUy3Ql>^hJgf7Y9&K#cG+^un4$rTN<=^>pfhDUYpGyp z=;K%|oPdPh$;pt^AZ#uok`Qj8vIZzvuo$&SUls!$r)Y@1WwfVTOYqr#ZdNRJroJc) z7vKUt94k@kn!uW1k`{vCf*mk?XaQ)e-Fw&_3>9;G1otzkMfec{7*$c5D#{zDm=3N6vDA|_bcwBE z3b72dZITG6j8=hKQp&}!tR-5jZE2)wXAG{ATT2irMQXvS@tKxF)yt4Fu4E1iB^e}; zM+hV+$xR7B=)gK6v=S2`14M0*T#z!V3W+4AYi}utg@cz`W*MfJ05T9P#w=Bn z5Ks{T83iH?k{)O0fRN~*JH3Ux7Y}1>wIK3*@3q+NJk^66-XRt-yrB#G)DKWOUbx6e zLMDg47_AnFjGfcqwkOp20Efo|(Kd)8j2#U0$}IB*O(STo2_p&$An3{Uxd7aAJK1_W zIs?FOF9~tHpPl(aLPW=NyJTbtW>DS2l(Y)5L?tkPYbprE-U(sm>Q6^%J4hOA!9&?1 z&eRtGN2(&7)(UBKKzY8I7*S$Ku?cVzZcXjWfp7srJdiO&$dEP|hB>~>$?PFu1TyPA z=641w1P+mEl#`lK!m<2HOdO-yq{(HNPIk7w|1&ljh3%?XD%Hf}^DuxFE7cW6QWw*6 zyF+|fUQiF;uJ_A!jL=CNr9hs z42la1_MJOA8+<23TfMD!Z<3eED1{Pos-hwb3RNnqK#Bqwf#&e-Y@?u&CQeS4Q$W)k z1|A}Kvk(KmVzmH0Cc5=jX**964}j1O>aqyr(ep5dp}`6AiVzk>5dliqBM){P ztc?o9dZP)D7DkAO7BGY^LnLfY%?Dz4D)zLuV&cL>pWhgUQ=yv|w2B0yey{+bN*8ih4CDrR&?8PL6ltdv z0{}?`gGjD-bQJ~14Br{3uuQ!1!-8c?_Dc(ha5=m)HP<`emDrw}sxl#hMU?WCoLFOg zb`-v=NM6~u>>gVvb8r*`&*(ZY9O0x>a`{ci%RPPpKKhFyf(j_HMHXI9+4)JpasX)q z_c{b^g_gKYz62X6oX literal 17525 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 filter(year == 2010) |> count() |> @@ -11,14 +11,14 @@ test_that("`collapse()` doesn't ping an API for type = `'occurrences-count'`", { }) test_that("atlas_counts works with no arguments", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count <- atlas_counts() expect_s3_class(count, c("tbl_df", "tbl", "data.frame")) expect_gt(count$count, 0) }) test_that("count() |> collect() works with no arguments", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count <- galah_call() |> count() |> collect() @@ -27,7 +27,7 @@ test_that("count() |> collect() works with no arguments", { }) test_that("`identify()` reduces the number of records returned by `count()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts_all <- galah_call() |> count() |> collect() @@ -40,7 +40,7 @@ test_that("`identify()` reduces the number of records returned by `count()`", { }) test_that("`filter()` works with dates", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> filter(species == "Cacatua galerita", eventDate >= "2023-01-07T00:00:00Z", @@ -51,7 +51,7 @@ test_that("`filter()` works with dates", { }) test_that("`galah_identify()` works with `atlas_counts()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts_all <- galah_call() |> count() |> collect() @@ -63,7 +63,7 @@ test_that("`galah_identify()` works with `atlas_counts()`", { }) test_that("`count()` handles multiple 'group by' variables", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> filter(year >= 2021) |> group_by(year, month, basisOfRecord) |> @@ -76,7 +76,7 @@ test_that("`count()` handles multiple 'group by' variables", { }) test_that("`count()` handles 'species' as a 'group by' variable", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> filter(year > 2020) |> identify("Perameles") |> @@ -90,7 +90,7 @@ test_that("`count()` handles 'species' as a 'group by' variable", { }) test_that("atlas_counts handles 'taxonConceptID' as a 'group by' variable", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("Perameles") |> filter(year >= 2015) |> @@ -104,7 +104,7 @@ test_that("atlas_counts handles 'taxonConceptID' as a 'group by' variable", { }) test_that("atlas_counts returns same result with filter using `,` and `&`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count_comma <- galah_call() |> filter(year >= 2010, year < 2020) |> count() |> @@ -118,7 +118,7 @@ test_that("atlas_counts returns same result with filter using `,` and `&`", { # Spatial not checked test_that("atlas_counts filters correctly with galah_geolocate/galah_polygon", { - skip_if_offline() + skip_if_offline(); skip_on_ci() wkt <- "POLYGON ((146.5425 -42.63203, 146.8312 -43.13203, 147.4085 -43.13203, 147.6972 -42.63203, 147.4085 -42.13203, 146.8312 -42.13203, 146.5425 -42.63203))" |> sf::st_as_sfc() base_query <- galah_call() |> @@ -136,7 +136,7 @@ test_that("atlas_counts filters correctly with galah_geolocate/galah_polygon", { }) test_that("atlas_counts filters correctly with galah_geolocate/galah_bbox/galah_radius", { - skip_if_offline() + skip_if_offline(); skip_on_ci() wkt <- "POLYGON ((146.5425 -42.63203, 146.8312 -43.13203, 147.4085 -43.13203, 147.6972 -42.63203, 147.4085 -42.13203, 146.8312 -42.13203, 146.5425 -42.63203))" |> sf::st_as_sfc() base_query <- galah_call() |> @@ -162,7 +162,7 @@ test_that("atlas_counts filters correctly with galah_geolocate/galah_bbox/galah_ }) test_that("atlas_counts returns species counts", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count_species <- galah_call(type = "species") |> count() |> collect() @@ -176,7 +176,7 @@ test_that("atlas_counts returns species counts", { }) test_that("species counts work with group_by()", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count_species <- galah_call(type = "species") |> identify("Crinia") |> filter(year >= 2020) |> @@ -203,7 +203,7 @@ test_that("species counts work with group_by()", { test_that("order of `group_by()` doesn't affect result in `atlas_counts()", { # This is a test for Issue #198 raised by @shandiya # https://github.com/AtlasOfLivingAustralia/galah-R/issues/198 - skip_if_offline() + skip_if_offline(); skip_on_ci() reg <- c("Gibson Desert", "Little Sandy Desert", "Southern Volcanic Plain", @@ -269,4 +269,4 @@ test_that("order of `group_by()` doesn't affect result in `atlas_counts()", { # FIXME: check non-piped args work # FIXME: check `galah_` functions work -# FIXME: check `atlas_counts` \ No newline at end of file +# FIXME: check `atlas_counts` diff --git a/tests/testthat/test-atlas_distributions.R b/tests/testthat/test-atlas_distributions.R index 9577a73c..d7381b12 100644 --- a/tests/testthat/test-atlas_distributions.R +++ b/tests/testthat/test-atlas_distributions.R @@ -1,5 +1,5 @@ # test_that("show_all_distributions() works", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # x <- show_all_distributions() # expect_s3_class(x, c("tbl_df", "tbl", "data.frame")) # expect_gte(nrow(x), 1000) @@ -11,7 +11,7 @@ # }) # # test_that("`request_data(type = 'distributions')` works with `identify()`", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # x <- galah_call(type = "distributions") |> # identify("Foa fo") |> # collect() @@ -29,7 +29,7 @@ # }) # # test_that("`request_data(type = 'distributions')` works with `filter()`", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # x <- galah_call(type = "distributions") |> # filter(id == 25239) |> # collect() @@ -47,7 +47,7 @@ # }) # # test_that("request_data(type = 'distributions') works without filters", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # x <- galah_call(type = "distributions") |> # collapse() # expect_s3_class(x, "query") @@ -59,9 +59,9 @@ # }) # # test_that("atlas_distributions() fails when both identify and filter are supplied", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # expect_error({galah_call() |> # identify("Foa fo") |> # filter(id == 25239) |> # atlas_distributions()}) -# }) \ No newline at end of file +# }) diff --git a/tests/testthat/test-atlas_media.R b/tests/testthat/test-atlas_media.R index 2300027b..14ef318b 100644 --- a/tests/testthat/test-atlas_media.R +++ b/tests/testthat/test-atlas_media.R @@ -3,7 +3,7 @@ test_that("atlas_media fails when no filters are provided", { }) test_that("`atlas_media()` works", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au") media_data <- galah_call() |> identify("Microseris lanceolata") |> @@ -15,7 +15,7 @@ test_that("`atlas_media()` works", { }) test_that("collect_media suggests `galah_config(directory =)` when a temp folder is set as the directory", { - skip_if_offline() + skip_if_offline(); skip_on_ci() atlas_query <- atlas_media( identify = galah_identify("Regent Honeyeater"), filter = galah_filter(year == 2012)) @@ -31,7 +31,7 @@ test_that("collect_media suggests `galah_config(directory =)` when a temp folder }) test_that("`collapse()` and `collect()` work for `type = 'media'`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() get_image_sizes <- function(dir){ paste0(dir, @@ -126,7 +126,7 @@ test_that("`collapse()` and `collect()` work for `type = 'media'`", { }) test_that("atlas_media gives a warning when old arguments are used", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au", atlas = "Australia") expect_error({ media_data <- atlas_media( @@ -137,7 +137,7 @@ test_that("atlas_media gives a warning when old arguments are used", { }) test_that("collect_media handles different file formats", { - skip_if_offline() + skip_if_offline(); skip_on_ci() media_dir <- "test_media" galah_config(email = "ala4r@ala.org.au", @@ -158,7 +158,7 @@ test_that("collect_media handles different file formats", { }) test_that("collect_media handles thumbnails", { - skip_if_offline() + skip_if_offline(); skip_on_ci() media_dir <- "test_media" galah_config(email = "ala4r@ala.org.au", directory = media_dir) @@ -180,4 +180,4 @@ unlink("Temp", recursive = TRUE) cache_dir <- tempfile() dir.create(cache_dir) galah_config(directory = cache_dir) -rm(cache_dir) \ No newline at end of file +rm(cache_dir) diff --git a/tests/testthat/test-atlas_occurrences.R b/tests/testthat/test-atlas_occurrences.R index ada8b686..178d63ed 100644 --- a/tests/testthat/test-atlas_occurrences.R +++ b/tests/testthat/test-atlas_occurrences.R @@ -24,7 +24,7 @@ test_that("atlas_occurrences gives a nice error for invalid emails", { }) test_that("collapse(type = 'occurrences') creates an object", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Perameles") |> collapse() @@ -34,7 +34,7 @@ test_that("collapse(type = 'occurrences') creates an object", { }) test_that("`compute(type = 'occurrences')` works", { - skip_if_offline() + skip_if_offline(); skip_on_ci() base_query <- galah_call() |> identify("Vulpes vulpes") |> filter(year <= 1900, @@ -61,7 +61,7 @@ test_that("`compute(type = 'occurrences')` works", { # test all filters and type of columns in one call test_that("atlas_occurrences accepts all narrowing functions inline", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expected_cols <- c("decimalLatitude", "decimalLongitude", "eventDate", "scientificName", "taxonConceptID", "recordID", "dataResourceName", "occurrenceStatus", "stateProvince", @@ -94,7 +94,7 @@ test_that("atlas_occurrences accepts all narrowing functions inline", { # repeat above using `galah_` functions test_that("atlas_occurrences accepts all narrowing functions in pipe", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expected_cols <- c("decimalLatitude", "decimalLongitude", "eventDate", "scientificName", "taxonConceptID", "recordID", "dataResourceName", "occurrenceStatus", "stateProvince", @@ -114,7 +114,7 @@ test_that("atlas_occurrences accepts all narrowing functions in pipe", { }) test_that("atlas_occurrences() and friends accept a file name", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # set up directory for testing purposes directory <- "TEMP" unlink(directory, recursive = TRUE) @@ -160,7 +160,7 @@ test_that("atlas_occurrences() errors with an invalid DOI", { }) test_that("atlas_occurrences downloads data from a DOI", { - skip_if_offline() + skip_if_offline(); skip_on_ci() doi <- "10.26197/ala.0c1e8744-a639-47f1-9a5f-5610017ba060" result1 <- atlas_occurrences(doi = doi) expect_s3_class(result1, c("tbl_df", "tbl", "data.frame" )) @@ -179,7 +179,7 @@ test_that("atlas_occurrences downloads data from a DOI", { }) test_that("`atlas_occurrences()` places DOI in `attr()` correctly", { - skip_if_offline() + skip_if_offline(); skip_on_ci() directory <- "TEMP" unlink(directory, recursive = TRUE) dir.create(directory) @@ -212,6 +212,7 @@ test_that("`atlas_occurrences()` places DOI in `attr()` correctly", { }) test_that("group_by works on occurrences", { + skip_if_offline(); skip_on_ci() # compare group_by with atlas_species x <- galah_call() |> filter(year == 2024, @@ -234,7 +235,7 @@ test_that("group_by works on occurrences", { }) test_that("atlas_occurrences() doesn't return secret information", { - skip_if_offline() + skip_if_offline(); skip_on_ci() RUN <- FALSE skip_if((!file.exists("testdata/SECRETS.txt") | !RUN), message = "Secret information not provided") @@ -266,4 +267,4 @@ test_that("atlas_occurrences() doesn't return secret information", { expect_equal(x, y) # reset galah_config(email = "ala4r@ala.org.au") -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-atlas_species.R b/tests/testthat/test-atlas_species.R index 53be0a09..d964630a 100644 --- a/tests/testthat/test-atlas_species.R +++ b/tests/testthat/test-atlas_species.R @@ -1,19 +1,19 @@ test_that("atlas_species fails nicely if no email is provided", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "", run_checks = TRUE) # run_checks = FALSE doesn't provide error message expect_error(atlas_species(identify = galah_identify("Osphranter"))) galah_config(email = "ala4r@ala.org.au") }) test_that("atlas_species returns a tibble", { - skip_if_offline() + skip_if_offline(); skip_on_ci() species <- atlas_species(identify = galah_identify("Osphranter")) expect_s3_class(species, c("tbl_df", "tbl", "data.frame")) expect_gt(nrow(species), 1) }) test_that("atlas_species returns correct results when piped", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) species <- galah_call() |> galah_identify("perameles") |> @@ -32,7 +32,7 @@ test_that("atlas_species returns correct results when piped", { }) test_that("atlas_species returns correct results filtered by galah_geolocate", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) wkt <- "POLYGON ((146.5425 -42.63203, 146.8312 -43.13203, 147.4085 -43.13203, 147.6972 -42.63203, 147.4085 -42.13203, 146.8312 -42.13203, 146.5425 -42.63203))" @@ -53,7 +53,7 @@ test_that("atlas_species returns correct results filtered by galah_geolocate", { }) test_that("atlas_species works when no species are present", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au") galah_config(run_checks = TRUE) result <- galah_call() |> @@ -64,7 +64,7 @@ test_that("atlas_species works when no species are present", { }) test_that("collapse -> compute -> collect workflow is functional", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au") query <- galah_call(method = "data", type = "species") |> @@ -86,7 +86,7 @@ test_that("collapse works when no `filter()` is supplied", { # NOTE: this test was added to check for the error: "`speciesID` is not a valid field" # this occurred when calling `atlas_species()` because `speciesID` is a facet, # but `group_by` wasn't being called, so checks weren't constructed properly - skip_if_offline() + skip_if_offline(); skip_on_ci() wkt <- "POLYGON((73.0 -53, 95.6 -11.5, 105.6 -10.1, 123 -12.1, 130.7 -9.5, 142.2 -9.8, 168.1 -29.05, 159.1 -54.9, 73.0 -53))" expect_no_error({x <- galah_call(type = "species") |> st_crop(wkt) |> @@ -95,7 +95,7 @@ test_that("collapse works when no `filter()` is supplied", { }) test_that("atlas_species reformats column names when empty tibble is returned", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) # No matching species expected, an empty tibble should be returned diff --git a/tests/testthat/test-atlas_taxonomy.R b/tests/testthat/test-atlas_taxonomy.R index 99ad7a34..e73c5e60 100644 --- a/tests/testthat/test-atlas_taxonomy.R +++ b/tests/testthat/test-atlas_taxonomy.R @@ -24,7 +24,7 @@ test_that("atlas_taxonomy requires a single taxon", { }) test_that("atlas_taxonomy makes a tree when piped", { - skip_if_offline() + skip_if_offline(); skip_on_ci() tree <- galah_call() |> identify("fungi") |> filter(rank >= phylum) |> @@ -35,7 +35,7 @@ test_that("atlas_taxonomy makes a tree when piped", { }) test_that("atlas_taxonomy example runs", { - skip_if_offline() + skip_if_offline(); skip_on_ci() df <- galah_call() |> galah_identify("chordata") |> galah_filter(rank == class) |> @@ -46,4 +46,4 @@ test_that("atlas_taxonomy example runs", { }) # FIXME: add test for non-piped usage of `atlas_taxonomy()` -# FIXME: add test for constrain_ids \ No newline at end of file +# FIXME: add test for constrain_ids diff --git a/tests/testthat/test-count_arrange_slice.R b/tests/testthat/test-count_arrange_slice.R index 348cb1a9..5238b98e 100644 --- a/tests/testthat/test-count_arrange_slice.R +++ b/tests/testthat/test-count_arrange_slice.R @@ -1,5 +1,5 @@ test_that("default is to arrange by decending order of count", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year) |> @@ -12,7 +12,7 @@ test_that("default is to arrange by decending order of count", { }) test_that("arrange in increasing order of count", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year) |> @@ -26,7 +26,7 @@ test_that("arrange in increasing order of count", { }) test_that("arrange in decreasing order of count using `desc()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year) |> @@ -40,7 +40,7 @@ test_that("arrange in decreasing order of count using `desc()`", { }) test_that("arrange in increasing order of year", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year) |> @@ -54,7 +54,7 @@ test_that("arrange in increasing order of year", { }) test_that("arrange in decreasing order of year using `desc()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year) |> @@ -68,7 +68,7 @@ test_that("arrange in decreasing order of year using `desc()`", { }) test_that("`arrange()` by `count` and `slice_head()` work together", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # get lower values (bottom 5) result <- galah_call() |> filter(year >= 2015) |> @@ -98,7 +98,7 @@ test_that("`arrange()` by `count` and `slice_head()` work together", { }) test_that("`arrange()` by `year` and `slice_head()` work together", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # get lower values (bottom 5) result <- galah_call() |> filter(year >= 2015) |> @@ -130,7 +130,7 @@ test_that("`arrange()` by `year` and `slice_head()` work together", { }) test_that("`group_by()` with multiple fields works with `slice_head()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2015) |> group_by(year, basisOfRecord) |> @@ -142,4 +142,4 @@ test_that("`group_by()` with multiple fields works with `slice_head()`", { expect_equal(colnames(result), c("year", "basisOfRecord", "count")) expect_true(all(xtabs(~result$year) <= 5)) # current year may not have all values yet }) - \ No newline at end of file + diff --git a/tests/testthat/test-galah_apply_profile.R b/tests/testthat/test-galah_apply_profile.R index f6bf475c..e21a66f4 100644 --- a/tests/testthat/test-galah_apply_profile.R +++ b/tests/testthat/test-galah_apply_profile.R @@ -1,5 +1,5 @@ test_that("galah_apply_profile filters counts", { - skip_if_offline() + skip_if_offline(); skip_on_ci() without_profile <- galah_call() |> count() |> collect() @@ -18,7 +18,7 @@ test_that("galah_apply_profile filters counts", { }) test_that("galah_apply_profile filters species", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au", atlas = "Australia", run_checks = FALSE) @@ -37,7 +37,7 @@ test_that("galah_apply_profile filters species", { }) test_that("galah_apply_profile filters occurrences", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au", atlas = "Australia", run_checks = FALSE) @@ -55,7 +55,7 @@ test_that("galah_apply_profile filters occurrences", { }) test_that("galah_apply_profile matches profile", { - skip_if_offline() + skip_if_offline(); skip_on_ci() profile_1 <- galah_apply_profile("ALA") profile_2 <- galah_apply_profile(AVH) expect_equal(profile_1[[1]], "ALA") @@ -63,7 +63,7 @@ test_that("galah_apply_profile matches profile", { }) test_that("`galah_apply_profile()` errors at `collapse()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) expect_error(request_data() |> apply_profile(whatever) |> @@ -74,7 +74,7 @@ test_that("`galah_apply_profile()` errors at `collapse()`", { }) test_that("galah_apply_profile allows only one profile at a time", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error(galah_apply_profile(ALA, CSDM), "Too many data profiles supplied.") -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-galah_call.R b/tests/testthat/test-galah_call.R index 6f5109bb..3221c53a 100644 --- a/tests/testthat/test-galah_call.R +++ b/tests/testthat/test-galah_call.R @@ -18,7 +18,7 @@ test_that("galah_call accepts method arg", { # prints properly? test_that("galah_call works with all `galah_` functions", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_identify("Litoria") |> galah_filter(year == 2021, cl22 == "Tasmania") |> @@ -31,7 +31,7 @@ test_that("galah_call works with all `galah_` functions", { }) test_that("galah_call works irrespective of `galah_` function order", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_apply_profile(ALA) |> galah_group_by(year, basisOfRecord) |> @@ -44,7 +44,7 @@ test_that("galah_call works irrespective of `galah_` function order", { }) test_that("repeated calls to `galah_identify` are added correctly", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_identify("Litoria") |> galah_identify("Aves") diff --git a/tests/testthat/test-galah_config.R b/tests/testthat/test-galah_config.R index 93c08eca..25510d27 100644 --- a/tests/testthat/test-galah_config.R +++ b/tests/testthat/test-galah_config.R @@ -16,7 +16,7 @@ test_that("galah_config creates nested folders where requested", { }) test_that("galah_config checks download_id", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(verbose = TRUE) expect_error(galah_config(download_reason_id = 17)) expect_error(galah_config(download_reason_id = "NOTHING")) @@ -27,7 +27,7 @@ test_that("galah_config checks download_id", { }) test_that("galah_config checks inputs", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error(galah_config(caching = "value")) expect_error(galah_config(verbose = "value")) expect_error(galah_config(email = 4)) @@ -50,4 +50,4 @@ test_that("galah_config can swap between atlases", { acronym = "GBIF", region = "Global")) galah_config(atlas = "ALA") -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-galah_filter.R b/tests/testthat/test-galah_filter.R index 59d27cd3..964584c9 100644 --- a/tests/testthat/test-galah_filter.R +++ b/tests/testthat/test-galah_filter.R @@ -9,7 +9,7 @@ test_that("galah_filter gives an error for single equals sign", { }) test_that("galah_filter works with assertions", { - skip_if_offline() + skip_if_offline(); skip_on_ci() count_all <- atlas_counts() |> pull(count) count_invalid_spp <- galah_call() |> @@ -30,7 +30,7 @@ test_that("galah_filter works with assertions", { }) test_that("galah_filter handles multiple assertions", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # OR statements all_records <- atlas_counts() |> pull(count) @@ -76,7 +76,7 @@ test_that("galah_filter handles multiple assertions", { }) test_that("galah_filter handles assertions and taxa", { - skip_if_offline() + skip_if_offline(); skip_on_ci() problem_families <- galah_call() |> filter(assertions == "INVALID_SCIENTIFIC_NAME") |> group_by(family) |> @@ -220,7 +220,7 @@ test_that("galah_filter returns error when equations are passed as a string", { # expect_true(grepl("2010", filters$query)) test_that("galah_filter handles taxonomic queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() filters <- galah_filter(taxonConceptID == search_taxa("Animalia")$taxon_concept_id) expect_equal(nrow(filters), 1) expect_false(grepl("search_taxa", filters$query)) @@ -235,7 +235,7 @@ test_that("galah_filter handles taxonomic queries when passed as a string", { }) test_that("galah_filter handles taxonomic exclusions", { - skip_if_offline() + skip_if_offline(); skip_on_ci() filters <- galah_filter( taxonConceptID == search_taxa("Animalia")$taxon_concept_id, taxonConceptID != search_taxa("Chordata")$taxon_concept_id) @@ -244,7 +244,7 @@ test_that("galah_filter handles taxonomic exclusions", { }) test_that("galah_filter handles lsid as an input", { - skip_if_offline() + skip_if_offline(); skip_on_ci() ids <- c("https://biodiversity.org.au/afd/taxa/0df99ece-1982-4605-a2b3-4fcb7660ee2b", "https://id.biodiversity.org.au/node/apni/2910467", "https://id.biodiversity.org.au/node/apni/291047") # wrong id @@ -306,7 +306,7 @@ test_that("galah_filter handles %in% even with multiple filters", { }) test_that("galah_filter parses fields correctly with is.na()", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_no_error(galah_call() |> galah_filter(is.na(decimalLongitude)) |> atlas_counts() @@ -335,7 +335,7 @@ test_that("galah_filter parses fields correctly with is.na()", { }) test_that("`galah_filter()` handles apostrophes (') correctly", { - skip_if_offline() + skip_if_offline(); skip_on_ci() names <- c("Australia's Virtual Herbarium", "iNaturalist observations", "iNaturalist research-grade observations") @@ -349,7 +349,7 @@ test_that("`galah_filter()` handles apostrophes (') correctly", { }) test_that("`galah_filter()` handles multiple values with brackets correctly", { - skip_if_offline() + skip_if_offline(); skip_on_ci() filter <- galah_filter( scientificName == c("Aviceda (Aviceda) subcristata", "Todiramphus (Todiramphus) sanctus"))$query @@ -371,7 +371,7 @@ test_that("galah_filter builds correct query with `!`, `%in%`, `c()` and `identi }) test_that("`galah_filter()` accepts {{}} on lhs of formula", { - skip_if_offline() + skip_if_offline(); skip_on_ci() field <- "species" result <- galah_call() |> galah_filter({{field}} == "Eolophus roseicapilla") |> @@ -387,7 +387,7 @@ test_that("`galah_filter()` accepts {{}} on lhs of formula", { }) test_that("`group_by()` works when > 1 `filter()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() chosen_species <- c("Eolophus roseicapilla", "Platycercus elegans") x <- request_data() |> filter(species == chosen_species) |> @@ -454,4 +454,4 @@ test_that("galah_filter handles `type = 'files'` correctly", { expect_equal(y$filter, x) z <- request_files() |> filter(media == x) expect_equal(y, z) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-galah_group_by.R b/tests/testthat/test-galah_group_by.R index 0c9edc89..aa640fd5 100644 --- a/tests/testthat/test-galah_group_by.R +++ b/tests/testthat/test-galah_group_by.R @@ -6,7 +6,7 @@ test_that("`group_by` fields are checked during `collapse()`", { group_by(random_invalid_name) |> collapse() }) - skip_if_offline() + skip_if_offline(); skip_on_ci() # using `count() |> collect()` is synonymous with `request_data(type = "occurrences-count") |> collect()` expect_error({ request_data() |> @@ -18,7 +18,7 @@ test_that("`group_by` fields are checked during `collapse()`", { }) test_that("grouped atlas_counts returns expected output", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("Mammalia") |> group_by(basisOfRecord) |> @@ -28,7 +28,7 @@ test_that("grouped atlas_counts returns expected output", { }) test_that("grouped atlas_counts returns expected output when limit != NULL", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("Mammalia") |> group_by(basisOfRecord) |> @@ -41,7 +41,7 @@ test_that("grouped atlas_counts returns expected output when limit != NULL", { }) test_that("atlas_counts returns all counts if no limit is provided", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> group_by(basisOfRecord) |> # NOTE: basisOfRecord chosen as prone to breaking atlas_counts() # this code; please do not change it! @@ -50,7 +50,7 @@ test_that("atlas_counts returns all counts if no limit is provided", { }) test_that("atlas_counts returns an empty tibble if number of records = 0", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> filter(year < 1900 & year > 2000) |> count() |> @@ -62,7 +62,7 @@ test_that("atlas_counts returns an empty tibble if number of records = 0", { }) test_that("grouped atlas_counts for species returns expected output", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("Mammalia") |> filter(year == 2020) |> @@ -74,7 +74,7 @@ test_that("grouped atlas_counts for species returns expected output", { }) test_that("group_by works for three groups", { - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("cacatuidae") |> filter(year >= 2020) |> @@ -90,7 +90,7 @@ test_that("group_by works for three groups", { test_that("group_by returns correct information when ID fields are requested", { # NOTE: previously these were parsed as the `label` for that field, not the # value itself, hence this test - skip_if_offline() + skip_if_offline(); skip_on_ci() counts <- galah_call() |> identify("pardalotus quadragintus") |> filter(year == 2023) |> @@ -103,10 +103,10 @@ test_that("group_by returns correct information when ID fields are requested", { }) test_that("group_by fails for four groups", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_call() |> identify("cacatuidae") |> filter(year >= 2020) |> group_by(year, month, basisOfRecord, stateProvince) |> expect_error() -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-galah_identify.R b/tests/testthat/test-galah_identify.R index b0ff7996..404f1642 100644 --- a/tests/testthat/test-galah_identify.R +++ b/tests/testthat/test-galah_identify.R @@ -40,7 +40,7 @@ test_that("galah_identify pipes correctly when taxa are partially returned", { }) test_that("galah_identify warns when taxa are partially returned", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_message( galah_call() |> galah_identify("Litoria", "blarghy") |> @@ -52,7 +52,7 @@ test_that("galah_identify warns when taxa are partially returned", { }) test_that("galah_identify warns when identifiers are partially returned", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) ids <- c("https://biodiversity.org.au/afd/taxa/0df99ece-1982-4605-a2b3-4fcb7660ee2b", "https://id.biodiversity.org.au/node/apni/2910467", @@ -71,7 +71,7 @@ test_that("galah_identify warns when identifiers are partially returned", { }) test_that("galah_identify truncates unmatched list of taxa at 3 ", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_message( galah_call() |> galah_identify("Litoria", "blarghy", "blorp", "florp", "skorp") |> @@ -87,7 +87,7 @@ test_that("galah_identify truncates unmatched list of taxa at 3 ", { }) test_that("galah_identify errors for deprecated `search = FALSE` argument", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) ids <- c("https://biodiversity.org.au/afd/taxa/0df99ece-1982-4605-a2b3-4fcb7660ee2b", "https://id.biodiversity.org.au/node/apni/2910467", @@ -103,7 +103,7 @@ test_that("galah_identify errors for deprecated `search = FALSE` argument", { }) test_that("galah_identify warns for deprecated `search = TRUE` argument", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(run_checks = TRUE) ids <- c("Litoria", "Crinia") expect_warning( diff --git a/tests/testthat/test-galah_select.R b/tests/testthat/test-galah_select.R index c9258891..7f97d45e 100644 --- a/tests/testthat/test-galah_select.R +++ b/tests/testthat/test-galah_select.R @@ -4,7 +4,7 @@ test_that("`galah_select()` doesn't return error when columns don't exist", { }) test_that("`galah_select()` triggers error during `compute()` when columns don't exist", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error( galah_call() |> identify("perameles") |> @@ -20,7 +20,7 @@ test_that("`galah_select()` triggers error during `compute()` when columns don't }) test_that("`galah_select()` returns requested columns", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(atlas = "Australia", email = "ala4r@ala.org.au", run_checks = FALSE) @@ -34,7 +34,7 @@ test_that("`galah_select()` returns requested columns", { }) test_that("`galah_select()` returns requested columns when piped", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au", run_checks = FALSE) query <- galah_call() |> identify("oxyopes dingo") |> @@ -46,7 +46,7 @@ test_that("`galah_select()` returns requested columns when piped", { }) test_that("`galah_select()` builds expected columns when group = basic", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(group = "basic") |> @@ -57,7 +57,7 @@ test_that("`galah_select()` builds expected columns when group = basic", { }) test_that("`galah_select()` builds expected columns when group = event", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(group = "event") |> @@ -69,7 +69,7 @@ test_that("`galah_select()` builds expected columns when group = event", { }) test_that("`galah_select()` accepts multiple groups", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(group = c("basic", "assertions")) |> @@ -81,7 +81,7 @@ test_that("`galah_select()` accepts multiple groups", { }) test_that("galah_select defaults to group = 'basic' when there are no args", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> collapse() @@ -91,7 +91,7 @@ test_that("galah_select defaults to group = 'basic' when there are no args", { }) test_that("galah_select works with group = 'taxonomy'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(group = "taxonomy") |> @@ -111,7 +111,7 @@ test_that("galah_select works with group = 'taxonomy'", { }) test_that("galah_select returns assertions + recordID when group = assertions", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(group = "assertions") |> @@ -122,7 +122,7 @@ test_that("galah_select returns assertions + recordID when group = assertions", }) test_that("galah_select combines requested columns and group columns", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(year, basisOfRecord, group = "basic") |> @@ -133,7 +133,7 @@ test_that("galah_select combines requested columns and group columns", { }) test_that("galah_select can use tidyselect::contains", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(tidyselect::contains("el")) |> @@ -146,7 +146,7 @@ test_that("galah_select can use tidyselect::contains", { }) test_that("galah_select can use tidyselect::starts_with", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(tidyselect::starts_with("el")) |> @@ -159,7 +159,7 @@ test_that("galah_select can use tidyselect::starts_with", { }) test_that("galah_select can use tidyselect::last_col", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(tidyselect::last_col()) |> @@ -170,7 +170,7 @@ test_that("galah_select can use tidyselect::last_col", { }) test_that("galah_select can use tidyselect::last_col & user-defined queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(year, basisOfRecord, tidyselect::last_col()) |> @@ -181,7 +181,7 @@ test_that("galah_select can use tidyselect::last_col & user-defined queries", { }) test_that("galah_select can use tidyselect::last_col & group", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("oxyopes dingo") |> select(tidyselect::last_col(), group = "basic") |> @@ -193,7 +193,7 @@ test_that("galah_select can use tidyselect::last_col & group", { }) test_that("galah_select warns for invalid field names when type = 'species'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_warning({galah_call(type = "species") |> identify("Crinia") |> select(an_unrecognised_field_name) |> @@ -201,7 +201,7 @@ test_that("galah_select warns for invalid field names when type = 'species'", { }) test_that("galah_select works for type = 'species' with no arguments", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call(type = "species") |> identify("Crinia") |> select() |> @@ -211,7 +211,7 @@ test_that("galah_select works for type = 'species' with no arguments", { }) test_that("galah_select works for type = 'species'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call(type = "species") |> identify("Crinia") |> select(counts) |> @@ -221,11 +221,11 @@ test_that("galah_select works for type = 'species'", { }) test_that("galah_select works for type = 'species' with group = 'taxonomy'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call(type = "species") |> identify("Crinia") |> select(counts, lists, group = "taxonomy") |> collect() expect_true(all(c("taxon_concept_id", "count", "kingdom", "phylum") %in% colnames(x))) expect_gt(nrow(x), 10) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-international-Austria.R b/tests/testthat/test-international-Austria.R index f76465db..a4d22004 100644 --- a/tests/testthat/test-international-Austria.R +++ b/tests/testthat/test-international-Austria.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Austria works", { }) test_that("show_all(assertions) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -17,7 +17,7 @@ test_that("show_all(assertions) works for Austria", { }) test_that("show_all(collections) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -28,7 +28,7 @@ test_that("show_all(collections) works for Austria", { }) test_that("show_all(datasets) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -39,7 +39,7 @@ test_that("show_all(datasets) works for Austria", { }) test_that("show_all(fields) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -50,7 +50,7 @@ test_that("show_all(fields) works for Austria", { }) test_that("show_all(licences) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(licences) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -62,7 +62,7 @@ test_that("show_all(licences) works for Austria", { }) test_that("show_all(lists) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(lists) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -73,7 +73,7 @@ test_that("show_all(lists) works for Austria", { }) test_that("show_all(providers) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -84,7 +84,7 @@ test_that("show_all(providers) works for Austria", { }) test_that("show_all(reasons) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -99,7 +99,7 @@ test_that("show_all(profiles) fails for Austria", { }) test_that("search_all(fields) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -108,7 +108,7 @@ test_that("search_all(fields) works for Austria", { }) test_that("search_all(taxa) works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Vulpes vulpes") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -117,7 +117,7 @@ test_that("search_all(taxa) works for Austria", { }) test_that("show_values works for fields for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- try({search_all(fields, "basis_of_record") |> show_values()}, silent = TRUE) @@ -127,7 +127,7 @@ test_that("show_values works for fields for Austria", { }) test_that("show_values works for lists for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- try({search_all(lists, "dr30") |> show_values()}, silent = TRUE) @@ -137,7 +137,7 @@ test_that("show_values works for lists for Austria", { }) test_that("atlas_counts works with type = 'occurrences' for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -146,7 +146,7 @@ test_that("atlas_counts works with type = 'occurrences' for Austria", { }) test_that("atlas_counts works with type = 'species' for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -156,7 +156,7 @@ test_that("atlas_counts works with type = 'species' for Austria", { ## FIXME: Only works when run_checks = TRUE test_that("atlas_counts works with galah_identify for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_identify("Mammalia") |> # run_checks = TRUE works atlas_counts() |> @@ -172,7 +172,7 @@ test_that("atlas_counts works with galah_identify for Austria", { }) test_that("atlas_counts works with group_by for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_filter(year >= 2020) |> galah_group_by(year) |> @@ -184,7 +184,7 @@ test_that("atlas_counts works with group_by for Austria", { }) test_that("atlas_species works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Austria", email = "ala4r@ala.org.au", @@ -203,7 +203,7 @@ test_that("atlas_species works for Austria", { ## FIXME: Test only works when run_checks = TRUE test_that("atlas_occurrences works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Austria", email = "ala4r@ala.org.au", @@ -240,7 +240,7 @@ test_that("atlas_occurrences works for Austria", { }) test_that("atlas_media() works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Austria", email = "ala4r@ala.org.au", @@ -272,7 +272,7 @@ test_that("atlas_media() works for Austria", { ## FIXME: atlas_taxonomy doesn't work test_that("atlas_taxonomy works for Austria", { - skip_if_offline() + skip_if_offline(); skip_on_ci() y <- galah_call() |> identify("Aves") |> filter(rank >= order) |> diff --git a/tests/testthat/test-international-Brazil.R b/tests/testthat/test-international-Brazil.R index 3e1db963..ca439ddb 100644 --- a/tests/testthat/test-international-Brazil.R +++ b/tests/testthat/test-international-Brazil.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Brazil works", { }) test_that("show_all(fields) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -15,7 +15,7 @@ test_that("show_all(fields) works for Brazil", { }) test_that("show_all(collections) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -24,7 +24,7 @@ test_that("show_all(collections) works for Brazil", { }) test_that("show_all(datasets) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -33,7 +33,7 @@ test_that("show_all(datasets) works for Brazil", { }) test_that("show_all(providers) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -46,7 +46,7 @@ test_that("show_all(reasons) fails for Brazil", { }) test_that("show_all(assertions) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -59,7 +59,7 @@ test_that("show_all(profiles) fails for Brazil", { }) test_that("show_all(lists) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(lists, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -68,7 +68,7 @@ test_that("show_all(lists) works for Brazil", { }) test_that("search_all(fields) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -77,7 +77,7 @@ test_that("search_all(fields) works for Brazil", { }) test_that("search_all(taxa) works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -86,7 +86,7 @@ test_that("search_all(taxa) works for Brazil", { }) test_that("show_values works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_fields("basis_of_record") |> show_values() |> try(silent = TRUE) @@ -103,7 +103,7 @@ test_that("show_values works for Brazil", { }) test_that("atlas_counts works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -112,7 +112,7 @@ test_that("atlas_counts works for Brazil", { }) test_that("atlas_counts works with type = 'species' for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -121,7 +121,7 @@ test_that("atlas_counts works with type = 'species' for Brazil", { }) test_that("atlas_counts works with galah_identify for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() query1 <- galah_call() |> galah_identify("Mammalia") |> count() |> @@ -145,7 +145,7 @@ test_that("atlas_counts works with galah_identify for Brazil", { }) test_that("atlas_counts works with group_by for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_filter(year >= 2020) |> galah_group_by(year) |> @@ -157,7 +157,7 @@ test_that("atlas_counts works with group_by for Brazil", { }) test_that("atlas_species works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Brazil", email = "ala4r@ala.org.au", @@ -175,7 +175,7 @@ test_that("atlas_species works for Brazil", { ## FIXME: Caused by taxonomic search issue test_that("atlas_occurrences works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Brazil", email = "ala4r@ala.org.au", @@ -209,7 +209,7 @@ test_that("atlas_occurrences works for Brazil", { # "Myrmecophaga tridactyla" # anteater test_that("atlas_media() works for Brazil", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Brazil", email = "ala4r@ala.org.au", @@ -237,4 +237,4 @@ test_that("atlas_media() works for Brazil", { unlink("temp", recursive = TRUE) }) -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-international-France.R b/tests/testthat/test-international-France.R index 4a58ad14..1e57ba99 100644 --- a/tests/testthat/test-international-France.R +++ b/tests/testthat/test-international-France.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = France works", { }) test_that("show_all(fields) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -15,7 +15,7 @@ test_that("show_all(fields) works for France", { }) test_that("show_all(collections) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -24,7 +24,7 @@ test_that("show_all(collections) works for France", { }) test_that("show_all(datasets) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -33,7 +33,7 @@ test_that("show_all(datasets) works for France", { }) test_that("show_all(providers) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -46,7 +46,7 @@ test_that("show_all(reasons) fails for France", { }) test_that("show_all(assertions) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -63,7 +63,7 @@ test_that("show_all(lists) fails for France", { }) test_that("search_all(fields) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -72,7 +72,7 @@ test_that("search_all(fields) works for France", { }) test_that("search_all(taxa) works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Vulpes vulpes") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -81,7 +81,7 @@ test_that("search_all(taxa) works for France", { }) test_that("show_values works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_fields("basisOfRecord") |> show_values() |> try(silent = TRUE) @@ -90,7 +90,7 @@ test_that("show_values works for France", { }) test_that("atlas_counts works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -99,7 +99,7 @@ test_that("atlas_counts works for France", { }) test_that("atlas_counts works with type = 'species' for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -108,7 +108,7 @@ test_that("atlas_counts works with type = 'species' for France", { }) test_that("atlas_counts works with galah_identify for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Mammalia") |> count() |> @@ -127,7 +127,7 @@ test_that("atlas_counts works with galah_identify for France", { }) test_that("atlas_counts works with group_by for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2018) |> group_by(year) |> @@ -140,7 +140,7 @@ test_that("atlas_counts works with group_by for France", { }) test_that("atlas_species works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(email = "ala4r@ala.org.au") x <- galah_call(type = "species") |> identify("Lagomorpha") |> @@ -154,7 +154,7 @@ test_that("atlas_species works for France", { }) test_that("atlas_occurrences works for France", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(atlas = "France", email = "ala4r@ala.org.au") base_query <- galah_call() |> @@ -187,4 +187,4 @@ test_that("atlas_occurrences works for France", { unlink("temp", recursive = TRUE) }) -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-international-GBIF.R b/tests/testthat/test-international-GBIF.R index 66f7956f..7e12f3a7 100644 --- a/tests/testthat/test-international-GBIF.R +++ b/tests/testthat/test-international-GBIF.R @@ -20,7 +20,7 @@ test_that("show_all(fields) works for GBIF", { }) test_that("show_all(collections) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) expect_equal(nrow(x), 10) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) @@ -36,7 +36,7 @@ test_that("show_all(collections) works for GBIF", { }) test_that("show_all(datasets) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) expect_equal(nrow(x), 10) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) @@ -47,7 +47,7 @@ test_that("show_all(datasets) works for GBIF", { }) test_that("show_all(providers) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) expect_equal(nrow(x), 10) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) @@ -76,7 +76,7 @@ test_that("show_all(lists) fails for GBIF", { }) test_that("search_all(taxa) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_taxa("Mammalia") expect_equal(nrow(x), 1) expect_gte(ncol(x), 1) @@ -87,21 +87,21 @@ test_that("search_all(taxa) works for GBIF", { galah_config(verbose = TRUE) test_that("search_all(datasets) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(datasets, "Mammals") expect_lte(nrow(x), 20) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) }) test_that("search_all(collections) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(collections, "Museum") expect_lte(nrow(x), 20) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) }) test_that("search_all(providers) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(providers, "Frog") expect_lte(nrow(x), 20) expect_true(inherits(x, c("tbl_df", "tbl", "data.frame"))) @@ -110,14 +110,14 @@ test_that("search_all(providers) works for GBIF", { galah_config(verbose = FALSE) test_that("search_all(fields) works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- search_all(fields, "year") expect_equal(nrow(result), 2) expect_true(inherits(result, c("tbl_df", "tbl", "data.frame"))) }) test_that("show_values works for GBIF fields", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search_fields("basisOfRecord") |> show_values() |> nrow() |> @@ -125,7 +125,7 @@ test_that("show_values works for GBIF fields", { }) test_that("atlas_counts works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_gt(atlas_counts()$count, 0) }) @@ -136,7 +136,7 @@ test_that("atlas_counts fails for GBIF when type = 'species'", { galah_config(run_checks = TRUE) test_that("`count()` works with `filter()` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # collapse x <- request_data() |> filter(year == 2010) |> @@ -158,7 +158,7 @@ test_that("`count()` works with `filter()` for GBIF", { }) test_that("`count` works with `identify` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # collapse x <- request_data() |> identify("Mammalia") |> @@ -180,7 +180,7 @@ test_that("`count` works with `identify` for GBIF", { }) test_that("`count` works with `group_by` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_call() |> identify("Litoria") |> filter(year >= 2020) |> @@ -217,7 +217,7 @@ test_that("`count` works with `group_by` for GBIF", { # FIXME: `check_fields()` not tested for GBIF - try sending invalid fields to `filter()` test_that("`count()` works with `galah_polygon()` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # errors when points given clockwise wkt <- "POLYGON((142.36 -29.01,142.74 -29.01,142.74 -29.39,142.36 -29.39,142.36 -29.01))" expect_error({galah_call() |> @@ -246,7 +246,7 @@ test_that("`count()` works with `galah_polygon()` for GBIF", { }) test_that("`count()` works with `galah_radius()` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # ditto for a point and radius result <- galah_call() |> identify("Mammalia") |> @@ -270,7 +270,7 @@ test_that("`count()` works with `galah_radius()` for GBIF", { }) test_that("`atlas_occurrences()` works with `galah_polygon()` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() wkt <- "POLYGON((142.36 -29.01,142.36 -29.39,142.74 -29.39,142.74 -29.01,142.36 -29.01))" base_query <- galah_call() |> identify("Mammalia") |> @@ -285,7 +285,7 @@ test_that("`atlas_occurrences()` works with `galah_polygon()` for GBIF", { }) test_that("`atlas_occurences()` works with `galah_radius()` for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() base_query <- galah_call() |> identify("Mammalia") |> galah_radius(lat = -33.7, @@ -307,7 +307,7 @@ test_that("`galah_select()` returns message for GBIF", { }) test_that("atlas_species works for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- request_data(type = "species") |> filter(year == 2010) |> identify("Litoria") |> @@ -331,7 +331,7 @@ test_that("atlas_species works for GBIF", { }) test_that("atlas_media fails for GBIF", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error({galah_call() |> galah_identify("perameles") |> atlas_media() @@ -339,7 +339,7 @@ test_that("atlas_media fails for GBIF", { }) test_that("`collapse()` et al. work for GBIF with `type = 'occurrences'`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # collapse base_query <- request_data() |> identify("Vulpes vulpes") |> diff --git a/tests/testthat/test-international-Guatemala.R b/tests/testthat/test-international-Guatemala.R index ed31c818..2919fa09 100644 --- a/tests/testthat/test-international-Guatemala.R +++ b/tests/testthat/test-international-Guatemala.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Guatemala works", { }) test_that("show_all(fields) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -18,7 +18,7 @@ test_that("show_all(fields) works for Guatemala", { }) test_that("show_all(collections) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -27,7 +27,7 @@ test_that("show_all(collections) works for Guatemala", { }) test_that("show_all(datasets) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -36,7 +36,7 @@ test_that("show_all(datasets) works for Guatemala", { }) test_that("show_all(providers) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -45,7 +45,7 @@ test_that("show_all(providers) works for Guatemala", { }) test_that("show_all(reasons) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -54,7 +54,7 @@ test_that("show_all(reasons) works for Guatemala", { }) test_that("show_all(assertions) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -71,7 +71,7 @@ test_that("show_all(lists) works for Guatemala", { }) test_that("search_all(fields) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -80,7 +80,7 @@ test_that("search_all(fields) works for Guatemala", { }) test_that("search_all(taxa) works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -89,7 +89,7 @@ test_that("search_all(taxa) works for Guatemala", { }) test_that("show_values works for fields for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "basis_of_record") |> show_values() |> try(silent = TRUE) @@ -99,7 +99,7 @@ test_that("show_values works for fields for Guatemala", { }) test_that("atlas_counts works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -108,7 +108,7 @@ test_that("atlas_counts works for Guatemala", { }) test_that("atlas_counts works with type = 'species' for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -119,7 +119,7 @@ test_that("atlas_counts works with type = 'species' for Guatemala", { ## FIXME: Both queries work, but results are notably different ## update 2023-11-03: still happens, but unclear if an API problem or a data problem # test_that("atlas_counts works with galah_identify for Guatemala", { -# skip_if_offline() +# skip_if_offline(); skip_on_ci() # result <- galah_call() |> # identify("Mammalia") |> # count() |> @@ -134,7 +134,7 @@ test_that("atlas_counts works with type = 'species' for Guatemala", { # }) test_that("atlas_counts works with group_by for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2000) |> group_by(basis_of_record) |> @@ -147,7 +147,7 @@ test_that("atlas_counts works with group_by for Guatemala", { }) test_that("atlas_counts works with group_by for Guatemala when count = 0", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Mammalia") |> filter(!is.na(all_image_url)) |> @@ -161,7 +161,7 @@ test_that("atlas_counts works with group_by for Guatemala when count = 0", { }) test_that("atlas_species fails for Guatemala due to unavailable API", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Guatemala", email = "test@ala.org.au", @@ -172,7 +172,7 @@ test_that("atlas_species fails for Guatemala due to unavailable API", { }) test_that("atlas_occurrences works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Guatemala", email = "test@ala.org.au", @@ -201,7 +201,7 @@ test_that("atlas_occurrences works for Guatemala", { }) test_that("atlas_media() works for Guatemala", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Guatemala", email = "test@ala.org.au", @@ -229,4 +229,4 @@ test_that("atlas_media() works for Guatemala", { unlink("temp", recursive = TRUE) }) -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-international-Portugal.R b/tests/testthat/test-international-Portugal.R index 78024a57..2176d8b4 100644 --- a/tests/testthat/test-international-Portugal.R +++ b/tests/testthat/test-international-Portugal.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Portugal works", { }) test_that("show_all(fields) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -15,7 +15,7 @@ test_that("show_all(fields) works for Portugal", { }) test_that("show_all(collections) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -24,7 +24,7 @@ test_that("show_all(collections) works for Portugal", { }) test_that("show_all(datasets) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -34,7 +34,7 @@ test_that("show_all(datasets) works for Portugal", { ## FIXME: No data returned test_that("show_all(providers) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -43,7 +43,7 @@ test_that("show_all(providers) works for Portugal", { }) test_that("show_all(reasons) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -52,7 +52,7 @@ test_that("show_all(reasons) works for Portugal", { }) test_that("show_all(assertions) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -69,7 +69,7 @@ test_that("show_all(lists) fails for Portugal", { }) test_that("search_all(fields) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -78,7 +78,7 @@ test_that("search_all(fields) works for Portugal", { }) test_that("search_all(taxa) works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -87,7 +87,7 @@ test_that("search_all(taxa) works for Portugal", { }) test_that("show_values works for fields for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "basis_of_record") |> show_values() |> try(silent = TRUE) @@ -97,7 +97,7 @@ test_that("show_values works for fields for Portugal", { }) test_that("atlas_counts works for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -106,7 +106,7 @@ test_that("atlas_counts works for Portugal", { }) test_that("atlas_counts works with type = 'species' for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -115,7 +115,7 @@ test_that("atlas_counts works with type = 'species' for Portugal", { }) test_that("atlas_counts works with galah_identify for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Mammalia") |> count() |> @@ -135,7 +135,7 @@ test_that("atlas_counts works with galah_identify for Portugal", { }) test_that("atlas_counts works with group_by for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2000) |> group_by(basis_of_record) |> @@ -151,14 +151,14 @@ test_that("atlas_counts works with group_by for Portugal", { # so we can't test downloads. This affects `atlas_occurrences()` and # `atlas_species()` test_that("atlas_species returns error for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error({galah_call(type = "species") |> identify("Carnivora") |> collect() }) }) test_that("atlas_occurrences returns error for Portugal", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error(atlas_occurrences( filter = galah_filter(year == 2020) )) @@ -166,4 +166,4 @@ test_that("atlas_occurrences returns error for Portugal", { # Note: atlas_media() should also work, in theory, but again am unable to test -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-international-Spain.R b/tests/testthat/test-international-Spain.R index 8c41763a..6e471df7 100644 --- a/tests/testthat/test-international-Spain.R +++ b/tests/testthat/test-international-Spain.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Spain works", { }) test_that("show_all(fields) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -18,7 +18,7 @@ test_that("show_all(fields) works for Spain", { }) test_that("show_all(licences) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(licences) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -27,7 +27,7 @@ test_that("show_all(licences) works for Spain", { }) test_that("show_all(collections) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -36,7 +36,7 @@ test_that("show_all(collections) works for Spain", { }) test_that("show_all(datasets) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -45,7 +45,7 @@ test_that("show_all(datasets) works for Spain", { }) test_that("show_all(providers) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -54,7 +54,7 @@ test_that("show_all(providers) works for Spain", { }) test_that("show_all(reasons) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -63,7 +63,7 @@ test_that("show_all(reasons) works for Spain", { }) test_that("show_all(assertions) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -72,7 +72,7 @@ test_that("show_all(assertions) works for Spain", { }) test_that("show_all(profiles) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(profiles) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -81,7 +81,7 @@ test_that("show_all(profiles) works for Spain", { }) test_that("show_all(lists) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(lists, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -90,7 +90,7 @@ test_that("show_all(lists) works for Spain", { }) test_that("search_all(fields) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -99,7 +99,7 @@ test_that("search_all(fields) works for Spain", { }) test_that("search_all(taxa) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -108,7 +108,7 @@ test_that("search_all(taxa) works for Spain", { }) test_that("search_all(taxa) works using data.frames for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, data.frame(kingdom = "Animalia", phylum = "Chordata")) |> @@ -119,7 +119,7 @@ test_that("search_all(taxa) works using data.frames for Spain", { }) test_that("search_all(identifiers) works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(identifiers, "359") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -128,7 +128,7 @@ test_that("search_all(identifiers) works for Spain", { }) test_that("show_values works for fields for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "basisOfRecord") |> show_values() |> try(silent = TRUE) @@ -138,7 +138,7 @@ test_that("show_values works for fields for Spain", { }) test_that("show_values works for profiles for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(profiles, "LA") |> show_values() |> try(silent = TRUE) @@ -148,7 +148,7 @@ test_that("show_values works for profiles for Spain", { }) test_that("atlas_counts works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -157,7 +157,7 @@ test_that("atlas_counts works for Spain", { }) test_that("atlas_counts works with type = 'species' for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -166,7 +166,7 @@ test_that("atlas_counts works with type = 'species' for Spain", { }) test_that("atlas_counts works with galah_identify for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Mammalia") |> count() |> @@ -186,7 +186,7 @@ test_that("atlas_counts works with galah_identify for Spain", { }) test_that("atlas_counts works with apply_profile for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() without_profile <- galah_call() |> count() |> collect() @@ -200,7 +200,7 @@ test_that("atlas_counts works with apply_profile for Spain", { }) test_that("atlas_counts works with group_by for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2000) |> group_by(basis_of_record) |> @@ -213,7 +213,7 @@ test_that("atlas_counts works with group_by for Spain", { }) test_that("atlas_counts works with apply_profile for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() without_profile <- galah_call() |> count() |> collect() @@ -227,7 +227,7 @@ test_that("atlas_counts works with apply_profile for Spain", { }) test_that("atlas_species works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Spain", email = "test@ala.org.au", @@ -243,7 +243,7 @@ test_that("atlas_species works for Spain", { }) test_that("galah_select works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- galah_select() y <- galah_select(basisOfRecord) expect_equal(length(x), 2) @@ -258,7 +258,7 @@ test_that("galah_select works for Spain", { }) test_that("atlas_occurrences works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Spain", email = "test@ala.org.au", @@ -277,7 +277,7 @@ test_that("atlas_occurrences works for Spain", { }) test_that("atlas_media() works for Spain", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Spain", email = "test@ala.org.au", diff --git a/tests/testthat/test-international-Sweden.R b/tests/testthat/test-international-Sweden.R index a2199e99..b720b5e9 100644 --- a/tests/testthat/test-international-Sweden.R +++ b/tests/testthat/test-international-Sweden.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = Sweden works", { }) test_that("show_all(collections) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -15,7 +15,7 @@ test_that("show_all(collections) works for Sweden", { }) test_that("show_all(datasets) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -24,7 +24,7 @@ test_that("show_all(datasets) works for Sweden", { }) test_that("show_all(fields) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -33,7 +33,7 @@ test_that("show_all(fields) works for Sweden", { }) test_that("show_all(licences) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(licences, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -43,7 +43,7 @@ test_that("show_all(licences) works for Sweden", { }) test_that("show_all(lists) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(lists, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -52,7 +52,7 @@ test_that("show_all(lists) works for Sweden", { }) test_that("show_all(providers) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -61,7 +61,7 @@ test_that("show_all(providers) works for Sweden", { }) test_that("show_all(reasons) fails for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -70,7 +70,7 @@ test_that("show_all(reasons) fails for Sweden", { }) test_that("show_all(assertions) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -79,7 +79,7 @@ test_that("show_all(assertions) works for Sweden", { }) test_that("show_all(profiles) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(profiles) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -88,7 +88,7 @@ test_that("show_all(profiles) works for Sweden", { }) test_that("search_all(fields) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -97,7 +97,7 @@ test_that("search_all(fields) works for Sweden", { }) test_that("search_all(taxa) works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -106,7 +106,7 @@ test_that("search_all(taxa) works for Sweden", { }) test_that("`search_taxa()` works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa("Vulpes vulpes") |> try(silent = TRUE) skip_if(inherits(taxa, "try-error"), message = "API not available") @@ -118,7 +118,7 @@ test_that("`search_taxa()` works for Sweden", { }) test_that("`search_taxa()` works for multiple queries in Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search_terms <- c("Rodentia", "Amphibia", "Serpentes") taxa <- search_taxa(search_terms) |> try(silent = TRUE) @@ -129,7 +129,7 @@ test_that("`search_taxa()` works for multiple queries in Sweden", { }) test_that("`search_taxa()` works for multiple ranks in Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- data.frame(genus = c("Asteraceae", "Pinus"), kingdom = "Plantae") |> search_taxa() |> try(silent = TRUE) @@ -139,7 +139,7 @@ test_that("`search_taxa()` works for multiple ranks in Sweden", { }) test_that("show_values works fields in Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_fields("basisOfRecord") |> show_values() |> try(silent = TRUE) @@ -148,7 +148,7 @@ test_that("show_values works fields in Sweden", { }) test_that("show_values works for lists in Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- try({search_all(lists, "dr156") |> show_values()}, silent = TRUE) @@ -158,7 +158,7 @@ test_that("show_values works for lists in Sweden", { }) test_that("show_values works for profiles in Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- try({search_all(profiles, "SBDI") |> show_values()}, silent = TRUE) @@ -168,7 +168,7 @@ test_that("show_values works for profiles in Sweden", { }) test_that("atlas_counts works with type = 'occurrences' for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -177,7 +177,7 @@ test_that("atlas_counts works with type = 'occurrences' for Sweden", { }) test_that("atlas_counts works with type = 'species' for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -186,7 +186,7 @@ test_that("atlas_counts works with type = 'species' for Sweden", { }) test_that("atlas_counts works with galah_identify for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_identify("Mammalia") |> atlas_counts() |> @@ -204,7 +204,7 @@ test_that("atlas_counts works with galah_identify for Sweden", { }) test_that("atlas_counts works with group_by for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_filter(year >= 2020) |> galah_group_by(year) |> @@ -216,7 +216,7 @@ test_that("atlas_counts works with group_by for Sweden", { }) test_that("atlas_counts works with apply_profile for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() without_profile <- galah_call() |> count() |> collect() @@ -230,7 +230,7 @@ test_that("atlas_counts works with apply_profile for Sweden", { }) test_that("atlas_species works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Sweden", email = "martinjwestgate@gmail.com", @@ -246,7 +246,7 @@ test_that("atlas_species works for Sweden", { }) test_that("atlas_occurrences works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Sweden", email = "martinjwestgate@gmail.com", @@ -273,7 +273,7 @@ test_that("atlas_occurrences works for Sweden", { }) test_that("atlas_media() works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Sweden", email = "martinjwestgate@gmail.com", @@ -292,7 +292,7 @@ test_that("atlas_media() works for Sweden", { }) test_that("collect_media() works for Sweden", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "Sweden", email = "martinjwestgate@gmail.com", @@ -336,4 +336,4 @@ test_that("collect_media() works for Sweden", { unlink("temp", recursive = TRUE) }) -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-international-UK.R b/tests/testthat/test-international-UK.R index 5908a37c..5e3783d7 100644 --- a/tests/testthat/test-international-UK.R +++ b/tests/testthat/test-international-UK.R @@ -6,7 +6,7 @@ test_that("swapping to atlas = United Kingdom works", { }) test_that("show_all(fields) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(fields) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -18,7 +18,7 @@ test_that("show_all(fields) works for UK", { }) test_that("show_all(collections) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(collections, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -27,7 +27,7 @@ test_that("show_all(collections) works for UK", { }) test_that("show_all(datasets) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(datasets, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -36,7 +36,7 @@ test_that("show_all(datasets) works for UK", { }) test_that("show_all(providers) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(providers, limit = 10) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -45,7 +45,7 @@ test_that("show_all(providers) works for UK", { }) test_that("show_all(reasons) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(reasons) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -54,7 +54,7 @@ test_that("show_all(reasons) works for UK", { }) test_that("show_all(assertions) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(assertions) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -67,7 +67,7 @@ test_that("show_all(profiles) fails for UK", { }) test_that("show_all(lists) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- show_all(lists) |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -76,7 +76,7 @@ test_that("show_all(lists) works for UK", { }) test_that("search_all(fields) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(fields, "year") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -85,7 +85,7 @@ test_that("search_all(fields) works for UK", { }) test_that("search_all(taxa) works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_all(taxa, "Mammalia") |> try(silent = TRUE) skip_if(inherits(x, "try-error"), message = "API not available") @@ -94,7 +94,7 @@ test_that("search_all(taxa) works for UK", { }) test_that("search_taxa works for multiple queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa(c("Vulpes vulpes", "Meles meles")) |> try(silent = TRUE) skip_if(inherits(taxa, "try-error"), message = "API not available") @@ -102,12 +102,12 @@ test_that("search_taxa works for multiple queries", { }) test_that("search_taxa doesn't break with typos", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_silent(search_taxa("Vlpes")) }) test_that("show_values works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_fields("basis_of_record") |> show_values() |> try(silent = TRUE) @@ -116,7 +116,7 @@ test_that("show_values works for UK", { }) test_that("show_list_values works for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- search_lists("dr556") |> show_values() |> try(silent = TRUE) @@ -125,7 +125,7 @@ test_that("show_list_values works for United Kingdom", { }) test_that("atlas_counts works with type = 'occurrences' for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts() |> pull(count) |> try(silent = TRUE) @@ -134,7 +134,7 @@ test_that("atlas_counts works with type = 'occurrences' for United Kingdom", { }) test_that("atlas_counts works with type = 'species' for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- atlas_counts(type = "species") |> pull(count) |> try(silent = TRUE) @@ -143,7 +143,7 @@ test_that("atlas_counts works with type = 'species' for United Kingdom", { }) test_that("atlas_counts works with galah_identify for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> identify("Vulpes") |> count() |> @@ -165,7 +165,7 @@ test_that("atlas_counts works with galah_identify for United Kingdom", { # possibly because name-matching is going wrong somewhere test_that("atlas_counts works with group_by for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> galah_filter(year >= 2020) |> galah_group_by(year) |> @@ -176,8 +176,8 @@ test_that("atlas_counts works with group_by for United Kingdom", { expect_equal(names(result), c("year", "count")) }) -test_that("atlas_species works for United Kingdom, but doesn't return data", { - skip_if_offline() +test_that("atlas_species works for United Kingdom", { + skip_if_offline(); skip_on_ci() galah_config( atlas = "United Kingdom", email = "ala4r@ala.org.au", @@ -188,12 +188,12 @@ test_that("atlas_species works for United Kingdom, but doesn't return data", { identify("Canidae") |> atlas_species() expect_s3_class(x, c("tbl_df", "tbl", "data.frame")) - expect_equal(nrow(x), 0) - expect_equal(ncol(x), 0) + expect_gte(nrow(x), 1) + expect_gte(ncol(x), 1) }) test_that("atlas_occurrences works for United Kingdom", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "United Kingdom", email = "ala4r@ala.org.au", @@ -240,7 +240,7 @@ test_that("atlas_occurrences works for United Kingdom", { }) test_that("atlas_media() works for UK", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config( atlas = "United Kingdom", email = "ala4r@ala.org.au", @@ -265,4 +265,4 @@ test_that("atlas_media() works for UK", { unlink("temp", recursive = TRUE) }) -galah_config(atlas = "Australia") \ No newline at end of file +galah_config(atlas = "Australia") diff --git a/tests/testthat/test-masked-functions.R b/tests/testthat/test-masked-functions.R index 94d4aa49..dd794e92 100644 --- a/tests/testthat/test-masked-functions.R +++ b/tests/testthat/test-masked-functions.R @@ -1,5 +1,5 @@ test_that("`identify` works identically to piped `galah_identify`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result1 <- galah_call() |> galah_identify("Litoria") result2 <- galah_call() |> identify("Litoria") expect_equal(result1, result2) @@ -31,14 +31,14 @@ test_that("`st_crop` works identically to piped `galah_polygon`", { }) test_that("`count` works identically to piped `atlas_counts`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result1 <- galah_call() |> galah_identify("Litoria") |> atlas_counts() result2 <- galah_call() |> identify("Litoria") |> count() |> collect() expect_equal(result1, result2) }) test_that("`slice_head` works for atlas_counts", { - skip_if_offline() + skip_if_offline(); skip_on_ci() result <- galah_call() |> filter(year >= 2010) |> group_by(year) |> @@ -46,4 +46,4 @@ test_that("`slice_head` works for atlas_counts", { count() |> collect() expect_equal(nrow(result), 5) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-request_metadata_unnest.R b/tests/testthat/test-request_metadata_unnest.R index 8a1badd0..798feb7c 100644 --- a/tests/testthat/test-request_metadata_unnest.R +++ b/tests/testthat/test-request_metadata_unnest.R @@ -1,5 +1,5 @@ test_that("request_metadata() |> unnest() works for type = 'fields'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # no filter provided causes an error expect_error({request_metadata() |> unnest() |> @@ -27,7 +27,7 @@ test_that("request_metadata() |> unnest() works for type = 'fields'", { }) test_that("request_metadata() |> unnest() works for type = 'lists'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- request_metadata() |> filter(list == dr947) |> unnest() |> @@ -44,7 +44,7 @@ test_that("request_metadata() |> unnest() works for type = 'lists'", { }) test_that("request_metadata() |> unnest() works for type = 'profiles'", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- request_metadata() |> unnest() |> filter(profile == "something") @@ -59,7 +59,7 @@ test_that("request_metadata() |> unnest() works for type = 'profiles'", { }) test_that("request_metadata() |> unnest() works for type = 'taxa' using `identify()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- request_metadata() |> identify("crinia") |> unnest() |> @@ -77,7 +77,7 @@ test_that("request_metadata() |> unnest() works for type = 'taxa' using `identif }) test_that("request_metadata() |> unnest() works for type = 'taxa' using `filter()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() lookup <- search_taxa("Crinia")$taxon_concept_id x <- request_metadata() |> filter(taxa == lookup) |> @@ -93,4 +93,4 @@ test_that("request_metadata() |> unnest() works for type = 'taxa' using `filter( expect_s3_class(z, c("tbl_df", "tbl", "data.frame")) expect_gte(ncol(z), 3) expect_gte(nrow(z), 10) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-search_all.R b/tests/testthat/test-search_all.R index 71a917c8..55bc0d2f 100644 --- a/tests/testthat/test-search_all.R +++ b/tests/testthat/test-search_all.R @@ -3,12 +3,12 @@ # (these are probably not built for many fast queries if output is large) test_that("search_all checks inputs, returns helpful error", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() expect_error(search_all(attributes, ""), "Unrecognised metadata requested") }) test_that("search_all returns correct output for type", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() fields <- search_all(fields, "year") reasons <- search_all(reasons, "genus") profiles <- search_all(profiles, "ala") @@ -28,13 +28,13 @@ test_that("search_all returns correct output for type", { }) test_that("search_all returns error when missing query", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() expect_error(search_all(profiles), "We didn't detect a search query") expect_error(search_all(fields, blah)) }) test_that("search_assertions returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_assertions() search <- search_assertions("INVALID") search2 <- search_all(assertions, "INVALID") @@ -50,7 +50,7 @@ test_that("search_assertions returns a filtered result", { }) test_that("search_apis returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_apis() search <- search_apis("image") search2 <- search_all(apis, "image") @@ -65,7 +65,7 @@ test_that("search_apis returns a filtered result", { }) test_that("search_atlases returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_atlases() search <- search_atlases("guat") search2 <- search_all(atlases, "guat") @@ -80,7 +80,7 @@ test_that("search_atlases returns a filtered result", { }) test_that("search_collections returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_collections() search <- search_collections("dna") search2 <- search_all(collections, "dna") @@ -95,7 +95,7 @@ test_that("search_collections returns a filtered result", { }) test_that("search_datasets returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_datasets() search <- search_datasets("endangered") search2 <- search_all(datasets, "endangered") @@ -110,7 +110,7 @@ test_that("search_datasets returns a filtered result", { }) test_that("search_fields returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_fields() search <- search_fields("precipitation") search2 <- search_all(fields, "precipitation") @@ -125,12 +125,12 @@ test_that("search_fields returns a filtered result", { }) test_that("search_fields helpful warning with blank argument", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() expect_error(search_fields(), "We didn't detect a search query.") }) test_that("search_licenses returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_licences() search <- search_licences("3.0") search2 <- search_all(licences, "3.0") @@ -145,7 +145,7 @@ test_that("search_licenses returns a filtered result", { }) test_that("search_lists returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_lists() search <- search_lists("threatened") search2 <- search_all(lists, "threatened") @@ -160,7 +160,7 @@ test_that("search_lists returns a filtered result", { }) test_that("search_reasons returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_reasons() search <- search_reasons("sci") search2 <- search_all(reasons, "sci") @@ -175,7 +175,7 @@ test_that("search_reasons returns a filtered result", { }) test_that("search_ranks returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_ranks() search <- search_ranks("kingdom") search2 <- search_all(ranks, "kingdom") @@ -190,7 +190,7 @@ test_that("search_ranks returns a filtered result", { }) test_that("search_profiles returns a filtered result", { - skip_on_cran(); skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_profiles() search <- search_profiles("base") search2 <- search_all(profiles, "base") @@ -205,7 +205,7 @@ test_that("search_profiles returns a filtered result", { }) test_that("search_providers returns a filtered result", { - skip_on_cran(); skip_if_offline(); skip_on_ci() + skip_if_offline(); skip_on_ci() all <- show_all_providers() search <- search_providers("inaturalist") search2 <- search_all(providers, "inaturalist") diff --git a/tests/testthat/test-search_taxa.R b/tests/testthat/test-search_taxa.R index c6388a53..d73ab68a 100644 --- a/tests/testthat/test-search_taxa.R +++ b/tests/testthat/test-search_taxa.R @@ -3,26 +3,26 @@ test_that("search_taxa checks inputs", { }) test_that("search_taxa works for simple queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa("Microseris lanceolata") expect_equal(nrow(taxa), 1) }) test_that("search_taxa works for multiple queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa(c("Eucalyptus", "Banksia", "Acacia")) expect_equal(nrow(taxa), 3) }) test_that("search_taxa handles data.frame input", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa( data.frame(genus = c("Banksia", "Microseris"), kingdom = "Plantae")) expect_equal(nrow(taxa), 2) }) test_that("search_taxa handles a mix of valid and invalid queries", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(verbose = TRUE) expect_message(taxa <- search_taxa(c("Eucalyptus", "Banksia", "Wattle", "wootle"))) expect_equal(nrow(taxa), 4) @@ -30,14 +30,14 @@ test_that("search_taxa handles a mix of valid and invalid queries", { }) test_that("search_taxa gives a message for invalid names", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(verbose = TRUE) expect_message(search_taxa("bad_term")) galah_config(verbose = FALSE) }) test_that("search_taxa searches using multiple ranks", { - skip_if_offline() + skip_if_offline(); skip_on_ci() taxa <- search_taxa(data.frame(genus = "Acacia", kingdom = "Plantae")) expect_s3_class(taxa, c("tbl_df", "tbl", "data.frame")) expect_equal(taxa$rank, "genus") @@ -45,7 +45,7 @@ test_that("search_taxa searches using multiple ranks", { }) test_that("`search_identifiers()` works via `search_all()`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() id <- "urn:lsid:biodiversity.org.au:afd.taxon:08b9a1f0-62ae-45ca-9208-e773b00021ed" search <- search_identifiers(id) search2 <- search_all(identifiers, id) @@ -57,7 +57,7 @@ test_that("`search_identifiers()` works via `search_all()`", { }) test_that("search_identifiers searches using identifier", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # check different types of id identifier <- c("urn:lsid:biodiversity.org.au:afd.taxon:08b9a1f0-62ae-45ca-9208-e773b00021ed", "NZOR-6-1742", "https://id.biodiversity.org.au/node/apni/2910467") @@ -67,7 +67,7 @@ test_that("search_identifiers searches using identifier", { }) test_that("search_identifiers works when one value is missing", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # check different types of id identifier <- c("urn:lsid:biodiversity.org.au:afd.taxon:08b9a1f0-62ae-45ca-9208-e773b00021ed", "something") @@ -79,24 +79,24 @@ test_that("search_identifiers works when one value is missing", { }) test_that("search_identifiers gives a message for invalid ids", { - skip_if_offline() + skip_if_offline(); skip_on_ci() galah_config(verbose = TRUE) expect_message(search_identifiers("1234")) galah_config(verbose = FALSE) }) test_that("search_taxa gives an error when homonyms are returned", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_warning(search_taxa("ACANTHOCEPHALA")) }) test_that("search_taxa handles name issues", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_warning(search_taxa("Microseris")) }) test_that("search_taxa handles multiple issues", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expected <- c("homonym, parentChildSynonym") result <- search_taxa("Gallinago sp.") @@ -111,7 +111,7 @@ test_that("search_taxa errors nicely when piped in galah_call", { }) test_that("`request_metadata()` works for `type = 'taxa'`", { - skip_if_offline() + skip_if_offline(); skip_on_ci() x <- request_metadata() |> identify("crinia") expect_s3_class(x, "metadata_request") @@ -123,4 +123,4 @@ test_that("`request_metadata()` works for `type = 'taxa'`", { z <- collect(y) expect_s3_class(z, c("tbl_df", "tbl", "data.frame")) expect_equal(nrow(z), 1) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-show_all.R b/tests/testthat/test-show_all.R index 1256524c..366dc045 100644 --- a/tests/testthat/test-show_all.R +++ b/tests/testthat/test-show_all.R @@ -40,4 +40,4 @@ test_that("all show_all() functions return correctly with all syntax", { expect_equal(syntax1, syntax3) expect_equal(nrow(limit_test), 3) })) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-show_values.R b/tests/testthat/test-show_values.R index 89fc68a5..963f02fe 100644 --- a/tests/testthat/test-show_values.R +++ b/tests/testthat/test-show_values.R @@ -1,5 +1,5 @@ test_that("show_values checks values", { - skip_if_offline() + skip_if_offline(); skip_on_ci() df <- tibble::tibble(x = c(1:2), y = c("a", "b")) wrong_type_search <- search_all(reasons, "sci") expect_error(show_values(), 'Missing information for values lookup.') @@ -8,7 +8,7 @@ test_that("show_values checks values", { }) test_that("show_values accepts search & show_all inputs from fields", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(lists, "EPBC act") filtered_show <- show_all(lists) |> dplyr::filter(species_list_uid == "dr656") @@ -21,7 +21,7 @@ test_that("show_values accepts search & show_all inputs from fields", { }) test_that("show_values accepts search & show_all inputs from profiles", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(profiles, "ALA") filtered_show <- show_all(profiles) |> dplyr::filter(shortName == "ALA") @@ -34,7 +34,7 @@ test_that("show_values accepts search & show_all inputs from profiles", { }) test_that("show_values accepts search & show_all inputs from lists", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(fields, "cl22") filtered_show <- show_all(fields) |> dplyr::filter(id == "year") @@ -47,13 +47,13 @@ test_that("show_values accepts search & show_all inputs from lists", { }) test_that("search_values returns helpful error when missing query", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expect_error(search_values(), "Missing information for values lookup") expect_error(search_all(fields, "cl22") |> search_values(), "didn't detect a search query") }) test_that("search_values returns filtered results for fields", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(fields, "cl22") values_search <- search |> search_values("new") values_show <- search |> show_values() @@ -67,7 +67,7 @@ test_that("search_values returns filtered results for fields", { }) test_that("search_values returns filtered results for profiles", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(profiles, "ALA") values_search <- search |> search_values("kingdom") values_show <- search |> show_values() @@ -81,7 +81,7 @@ test_that("search_values returns filtered results for profiles", { }) test_that("search_values returns filtered results for lists", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search <- search_all(lists, "ALA") values_search <- search |> search_values("frog") values_show <- search |> show_values() @@ -95,7 +95,7 @@ test_that("search_values returns filtered results for lists", { }) test_that("show_values & search_values return number of matched fields", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search1 <- search_fields("year") search2 <- search_fields("basisOfRecord") expect_message(search1 |> show_values(), "Showing values for 'year'") @@ -103,7 +103,7 @@ test_that("show_values & search_values return number of matched fields", { }) test_that("search_values specifies matched field", { - skip_if_offline() + skip_if_offline(); skip_on_ci() search1 <- search_fields("year") search2 <- search_fields("state") search3 <- search_profiles("ALA") @@ -116,7 +116,7 @@ test_that("search_values specifies matched field", { }) test_that("show_values returns unformatted names", { - skip_if_offline() + skip_if_offline(); skip_on_ci() expected <- tibble(basisOfRecord = c("HUMAN_OBSERVATION", "PRESERVED_SPECIMEN")) search <- search_all(fields, "basisOfRecord") @@ -125,7 +125,7 @@ test_that("show_values returns unformatted names", { }) test_that("unnest syntax works", { - skip_if_offline() + skip_if_offline(); skip_on_ci() # fields x <- request_metadata() |> filter(field == "cl22") |> From 82974f5a07b0c4c60b1cf369339d7d5de35c223d Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Tue, 4 Feb 2025 16:35:18 +1100 Subject: [PATCH 19/27] fix incorrectly failing test for search_all the lookup function for search_all checks all character columns, but this function was only testing one, hence failure --- tests/testthat/test-search_all.R | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/testthat/test-search_all.R b/tests/testthat/test-search_all.R index 55bc0d2f..e65f0bd1 100644 --- a/tests/testthat/test-search_all.R +++ b/tests/testthat/test-search_all.R @@ -146,17 +146,25 @@ test_that("search_licenses returns a filtered result", { test_that("search_lists returns a filtered result", { skip_if_offline(); skip_on_ci() - all <- show_all_lists() - search <- search_lists("threatened") + all_lists <- show_all_lists() + search1 <- search_lists("threatened") search2 <- search_all(lists, "threatened") - search_result_check <- all(grepl(pattern = "threatened", search$listName, - ignore.case = TRUE)) - - expect_lt(nrow(search), nrow(all)) - expect_equal(attributes(search)$call, "lists") - expect_s3_class(search, c("tbl_df", "tbl", "data.frame")) + # check whether search_lists is correctly detecting the target string + chr_lookup <- purrr::map(colnames(search1), is.character) |> + unlist() + chr_cols <- colnames(search1)[chr_lookup] + contains_threatened <- purrr::map(chr_cols, \(a){ + grepl(pattern = "threatened", search1[[a]], ignore.case = TRUE) + }) + search_result_check <- purrr::map( + purrr::list_transpose(contains_threatened), any) |> + unlist() |> + all() + expect_lt(nrow(search1), nrow(all_lists)) + expect_equal(attributes(search1)$call, "lists") + expect_s3_class(search1, c("tbl_df", "tbl", "data.frame")) expect_true(search_result_check) - expect_equal(search, search2) + expect_equal(search1, search2) }) test_that("search_reasons returns a filtered result", { From d6b3a9d0dcdbc4f5df6ec8ec769ee3a473085367 Mon Sep 17 00:00:00 2001 From: Dax Kellie Date: Wed, 5 Feb 2025 12:03:44 +1100 Subject: [PATCH 20/27] Add R-CMD check to gh actions + badge --- .github/workflows/R-CMD-check.yaml | 33 ++++++++++++++++++++++++++++++ README.Rmd | 1 + 2 files changed, 34 insertions(+) create mode 100644 .github/workflows/R-CMD-check.yaml diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 00000000..f087a353 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,33 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/README.Rmd b/README.Rmd index 35b31907..d74ff021 100644 --- a/README.Rmd +++ b/README.Rmd @@ -19,6 +19,7 @@ knitr::opts_chunk$set( [![CRAN status](https://www.r-pkg.org/badges/version/galah)](https://cran.r-project.org/package=galah) [![Codecov test coverage](https://codecov.io/gh/AtlasOfLivingAustralia/galah-R/branch/main/graph/badge.svg)](https://app.codecov.io/gh/AtlasOfLivingAustralia/galah-R?branch=main) +[![R-CMD-check](https://github.com/AtlasOfLivingAustralia/galah-R/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/AtlasOfLivingAustralia/galah-R/actions/workflows/R-CMD-check.yaml) From ed947cf622f0432eca4e9869181b301c198c7931 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Wed, 5 Feb 2025 16:13:38 +1100 Subject: [PATCH 21/27] Add `read_zip()` function for loading local files - increment version number to 2.1.1 - export `read_zip()` for > reproducibility - remove rarely used functions from NAMESPACE, use package::fun syntax for these cases instead as per R packages advice (https://r-pkgs.org/dependencies-in-practice.html#sec-dependencies-in-imports-r-code) --- DESCRIPTION | 2 +- NAMESPACE | 11 +---- R/atlas_citation.R | 63 +++++++++++++++++------- R/atlas_media.R | 3 +- R/atlas_occurrences.R | 18 +++---- R/check.R | 10 ++-- R/collapse_unnest.R | 1 - R/collect_occurrences.R | 64 ++++-------------------- R/collect_species.R | 10 ++-- R/collect_taxa.R | 3 +- R/galah_config.R | 2 +- R/parse_checks.R | 3 +- R/read_zip.R | 106 ++++++++++++++++++++++++++++++++++++++++ R/search_all.R | 3 +- R/url_lookup.R | 5 +- man/atlas_.Rd | 18 +++---- man/read_zip.Rd | 38 ++++++++++++++ 17 files changed, 234 insertions(+), 126 deletions(-) create mode 100644 R/read_zip.R create mode 100644 man/read_zip.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 315195fd..04eaaabc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: galah Type: Package Title: Biodiversity Data from the GBIF Node Network -Version: 2.1.0 +Version: 2.1.1 Authors@R: c(person(given = "Martin", family = "Westgate", diff --git a/NAMESPACE b/NAMESPACE index ee3512b6..2fcfc069 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -61,6 +61,7 @@ export(galah_select) export(geolocate) export(group_by) export(identify) +export(read_zip) export(request_data) export(request_files) export(request_metadata) @@ -163,9 +164,6 @@ importFrom(purrr,pluck_depth) importFrom(purrr,rate_backoff) importFrom(purrr,rate_delay) importFrom(purrr,rate_sleep) -importFrom(readr,cols) -importFrom(readr,read_csv) -importFrom(readr,read_tsv) importFrom(rlang,.data) importFrom(rlang,abort) importFrom(rlang,as_label) @@ -221,10 +219,3 @@ importFrom(stringr,str_to_title) importFrom(stringr,str_trim) importFrom(tibble,as_tibble) importFrom(tibble,tibble) -importFrom(tidyr,drop_na) -importFrom(tidyr,unnest_longer) -importFrom(tidyselect,eval_select) -importFrom(utils,URLdecode) -importFrom(utils,URLencode) -importFrom(utils,adist) -importFrom(utils,unzip) diff --git a/R/atlas_citation.R b/R/atlas_citation.R index eaa80607..a22447f9 100644 --- a/R/atlas_citation.R +++ b/R/atlas_citation.R @@ -15,20 +15,40 @@ #' @export atlas_citation <- function(data) { + # get basic information from file + modified_date <- attributes(data)$modified_date doi <- attributes(data)$doi + citation <- attributes(data)$citation + search_url <- attributes(data)$search_url + + # get existing citation info + r_citation <- citation() |> + print(style = 'text') |> + utils::capture.output() |> + glue_collapse(sep = " ") + galah_citation <- citation(package = "galah") |> + print(style = 'text') |> + utils::capture.output() |> + glue_collapse(sep = " ") + + # set case when DOI is missing if (is.null(doi)) { - bullets <- c( - "This data does not have a DOI attached.", - i = "Did you set `atlas_occurrences(mint_doi = TRUE)`?", - i = "`atlas_citation` extracts this citation info when present." - ) - warn(bullets, call = caller_env()) - glue("Please consider citing R & galah. To do so, call: - citation() - citation('galah')") - }else{ - current_date <- format(Sys.Date(), "%e %B %Y") |> - trimws() + if(!is.null(citation)){ + glue(" + The citation for this dataset is: + + {citation} + + ") |> + cat() + }else{ + bullets <- c( + "This dataset does not have any citation information attached.", + i = "Please consider checking the atlas in question for their citation guidelines" + ) + warn(bullets, call = caller_env()) + } + }else{ # i.e. if DOI present if(grepl("10.26197/ala.", doi)){ org_text <- "Atlas of Living Australia" description <- "Occurrence download" @@ -36,12 +56,19 @@ atlas_citation <- function(data) { org_text <- "GBIF.org" description <- "GBIF Occurrence Download" } + glue(" - {org_text} ({current_date}) {description} {doi} - - Please consider citing R & galah, in addition to your dataset. To do so, call: - citation() - citation('galah') - ") + The citation for this dataset is: + + {org_text} ({modified_date}) {description} {doi} + + ") |> cat() } + glue(" + + Please consider citing R & galah, in addition to your dataset: + + {r_citation} + + {galah_citation}") } diff --git a/R/atlas_media.R b/R/atlas_media.R index f02208cf..5bcc39d0 100644 --- a/R/atlas_media.R +++ b/R/atlas_media.R @@ -11,7 +11,6 @@ #' @importFrom purrr pluck #' @importFrom rlang abort #' @importFrom stringr str_remove -#' @importFrom tidyr unnest_longer #' @export atlas_media <- function(request = NULL, identify = NULL, @@ -94,7 +93,7 @@ atlas_media <- function(request = NULL, # get occurrences occ <- query_collapse |> collect(wait = TRUE) |> - unnest_longer(col = any_of(present_fields)) + tidyr::unnest_longer(col = any_of(present_fields)) if(!any(colnames(occ) == "all_image_url")){ occ$media_id <- build_media_id(occ) diff --git a/R/atlas_occurrences.R b/R/atlas_occurrences.R index fca50644..9c4c2458 100644 --- a/R/atlas_occurrences.R +++ b/R/atlas_occurrences.R @@ -1,15 +1,15 @@ #' Retrieve a database query #' #' @description -#' An alternative to using `collect()` at the end of a query pipe is to -#' call a function with the `atlas_` prefix. These solutions are basically -#' synonymous, but `atlas_` functions differ in two ways: +#' An alternative to using \code{\link[=collect.data_request]{collect()}} at the +#' end of a query pipe is to call a function with the `atlas_` prefix. These +#' solutions are basically synonymous, but `atlas_` functions differ in two ways: #' #' * They have the ability to accept `filter`, `select` etc as arguments, -#' rather than within a pipe, but **only** when using the `galah_` forms of -#' those functions (e.g. `galah_filter()`). +#' rather than within a pipe; but **only** when using the `galah_` forms of +#' those functions (e.g. [galah_filter()]). #' * `atlas_` functions do not require you to specify the `method` or `type` -#' arguments to `galah_call()`, as they are more specific in what data are +#' arguments to [galah_call()], as they are more specific in what data are #' being requested. #' #' @name atlas_ @@ -76,7 +76,7 @@ #' # Get a list of species within genus "Heleioporus" #' # (every row is a species with associated taxonomic data) #' galah_call() |> -#' galah_identify("Heleioporus") |> +#' identify("Heleioporus") |> #' atlas_species() #' #' # Download Regent Honeyeater records with multimedia attached @@ -88,8 +88,8 @@ #' #' # Get a taxonomic tree of *Chordata* down to the class level #' galah_call() |> -#' galah_identify("chordata") |> -#' galah_filter(rank == class) |> +#' identify("chordata") |> +#' filter(rank == class) |> #' atlas_taxonomy() #' } #' @export diff --git a/R/check.R b/R/check.R index 9e007eb5..5af19d91 100644 --- a/R/check.R +++ b/R/check.R @@ -130,7 +130,6 @@ check_filter_tibbles <- function(x){ # where x is a list of tibbles #' @importFrom glue glue_data #' @importFrom httr2 url_parse #' @importFrom rlang format_error_bullets -#' @importFrom tidyr drop_na #' @noRd #' @keywords Internal check_fields <- function(.query) { @@ -151,7 +150,7 @@ check_fields <- function(.query) { returned_invalid <- tibble( function_name = c("`galah_filter()`", "`galah_group_by()`"), fields = check_result) |> - drop_na() + tidyr::drop_na() glue_template <- "{returned_invalid$function_name}: {returned_invalid$fields}" invalid_fields_message <- glue_data(returned_invalid, glue_template, .na = "") @@ -435,7 +434,7 @@ check_identifiers_la <- function(.query, error_call = caller_env()){ metadata_lookup <- grepl("^metadata/taxa", names(.query)) if(any(metadata_lookup)){ identifiers <- .query[[which(metadata_lookup)[1]]] - taxa_id <- URLencode(identifiers$taxon_concept_id[1], + taxa_id <- utils::URLencode(identifiers$taxon_concept_id[1], reserved = TRUE) .query$url[1] <- sub("%60TAXON_PLACEHOLDER%60", taxa_id, .query$url[1]) }else{ @@ -711,7 +710,6 @@ check_reason <- function(.query, error_call = caller_env()){ #' @importFrom httr2 url_parse #' @importFrom httr2 url_build #' @importFrom rlang is_quosure -#' @importFrom tidyselect eval_select #' @noRd #' @keywords Internal check_select <- function(.query){ @@ -735,7 +733,7 @@ check_select <- function(.query){ if(length(group) > 0){ group_cols <- lapply(group, preset_groups) |> unlist() - group_names <- eval_select(all_of(group_cols), data = df) |> + group_names <- tidyselect::eval_select(all_of(group_cols), data = df) |> names() # note: technically `group_names` and `group_cols` are identical # BUT `eval_select()` will fail if invalid columns are given @@ -748,7 +746,7 @@ check_select <- function(.query){ unlist() dots <- .query$select[check_quosures] dot_names <- lapply(dots, function(a){ - eval_select(a, data = df) |> + tidyselect::eval_select(a, data = df) |> names() }) |> unlist() diff --git a/R/collapse_unnest.R b/R/collapse_unnest.R index ae2ccdc6..22069448 100644 --- a/R/collapse_unnest.R +++ b/R/collapse_unnest.R @@ -62,7 +62,6 @@ collapse_profiles_unnest <- function(.query){ #' Internal function to `collapse()` for #' `request_metadata(type = "taxa") |> unnest()` #' @importFrom rlang abort -#' @importFrom utils URLencode #' @noRd #' @keywords Internal collapse_taxa_unnest <- function(.query){ diff --git a/R/collect_occurrences.R b/R/collect_occurrences.R index 8c082f5b..fb00a962 100644 --- a/R/collect_occurrences.R +++ b/R/collect_occurrences.R @@ -1,14 +1,17 @@ #' workhorse function to get occurrences from an atlas -#' @noRd #' @param .query an object of class `data_response`, created using #' `compute.data_request()` #' @param wait logical; should we ping the API until successful? Defaults to #' FALSE -#' @keywords Internal +#' @param file character; optional name for the downloaded file. Defaults to +#' `data` followed by the system time in `%Y-%m-%d_%H-%M-%S` format, with a +#' `.zip` suffix. #' @importFrom potions pour #' @importFrom rlang abort #' @importFrom rlang inform #' @importFrom tibble tibble +#' @noRd +#' @keywords Internal collect_occurrences <- function(.query, wait, file = NULL){ switch(pour("atlas", "region"), "Austria" = collect_occurrences_direct(.query, file = file), @@ -23,7 +26,7 @@ collect_occurrences_direct <- function(.query, file){ .query$download <- TRUE .query$file <- check_download_filename(file) query_API(.query) - result <- load_zip(.query$file) + result <- read_zip(.query$file) if(is.null(result)){ inform("Download failed") return(tibble()) @@ -52,7 +55,7 @@ collect_occurrences_default <- function(.query, wait, file){ download = TRUE) new_object$file <- check_download_filename(file) query_API(new_object) - result <- load_zip(new_object$file) + result <- read_zip(new_object$file) }else{ return(download_response) } @@ -88,60 +91,11 @@ collect_occurrences_doi <- function(.query, error_call = caller_env()) { .query$file <- check_download_filename(file) query_API(.query) - result <- load_zip(.query$file) + result <- read_zip(.query$file) if(is.null(result)){ inform("Download failed.") tibble() }else{ result } -} - -#' Internal function to load zip files, without unzipping them first -#' @importFrom dplyr bind_rows -#' @importFrom readr cols -#' @importFrom readr read_csv -#' @importFrom readr read_tsv -#' @importFrom utils unzip -#' @noRd -#' @keywords Internal -load_zip <- function(cache_file){ - # get names of files stored in .zip - all_files <- unzip(cache_file, list = TRUE)$Name - # zip files contain a lot of metadata that `galah` does not import - # import only those files that meet our criteria for 'data' - if(is_gbif()){ - available_files <- all_files[grepl(".csv$", all_files)] - result <- unz(description = cache_file, # require lapply? - filename = available_files) |> - read_tsv(col_types = cols()) |> - suppressWarnings() - # Note: DOIs for GBIF are stored in `compute()` stage, not in the zip file - }else{ - available_files <- all_files[grepl(".csv$", all_files) & - grepl("^data|records", all_files)] - result <- lapply(available_files, - function(a, x){ - # create connection to a specific file within zip - conn <- unz(description = x, - filename = a, - open = "rb") - out <- read_csv(conn, - col_types = cols()) |> - suppressWarnings() - close(conn) - return(out) - }, x = cache_file) |> - bind_rows() - # # add doi when mint_doi = TRUE - if(any(all_files == "doi.txt")){ - conn <- unz(description = cache_file, - filename = "doi.txt", - open = "rb") - attr(result, "doi") <- readr::read_file(conn) |> - sub("\\n$", "", x = _) - close(conn) - } - } - result -} +} \ No newline at end of file diff --git a/R/collect_species.R b/R/collect_species.R index 92e3327e..9502ee04 100644 --- a/R/collect_species.R +++ b/R/collect_species.R @@ -7,11 +7,11 @@ collect_species <- function(.query, file = NULL){ }else{ .query$file <- check_download_filename(file, ext = "csv") query_API(.query) - read_csv(.query$file, - col_names = get_clean_colnames(.query$file, - facet = .query$group_by$name), - col_types = cols(), - skip = 1) + readr::read_csv(.query$file, + col_names = get_clean_colnames(.query$file, + facet = .query$group_by$name), + col_types = cols(), + skip = 1) } } diff --git a/R/collect_taxa.R b/R/collect_taxa.R index 484de075..ad9f75b4 100644 --- a/R/collect_taxa.R +++ b/R/collect_taxa.R @@ -105,7 +105,6 @@ collect_taxa_gbif <- function(.query){ #' Internal function to do cleaning #' @param result a list from a taxonomic web service #' @importFrom purrr pluck -#' @importFrom utils adist #' @noRd #' @keywords Internal clean_la_taxa <- function(result, search_terms){ @@ -131,7 +130,7 @@ clean_la_taxa <- function(result, search_terms){ } else { # e.g. France taxon_names <- unlist(lapply(list_of_results, function(b){b$scientificName})) } - string_distances <- adist( + string_distances <- utils::adist( tolower(search_terms), tolower(taxon_names))[1, ] min_distance <- which(string_distances == min(string_distances)) diff --git a/R/galah_config.R b/R/galah_config.R index 644c1073..d439b767 100644 --- a/R/galah_config.R +++ b/R/galah_config.R @@ -289,7 +289,7 @@ configure_atlas <- function(query){ comparison <- do.call(c, node_metadata) comparison <- comparison[!is.na(comparison)] |> as.character() - lookup <- adist(query, comparison, ignore.case = TRUE)[1, ] + lookup <- utils::adist(query, comparison, ignore.case = TRUE)[1, ] if(all(lookup > 2)){ bullets <- c( diff --git a/R/parse_checks.R b/R/parse_checks.R index 8fa2d933..f2bcc024 100644 --- a/R/parse_checks.R +++ b/R/parse_checks.R @@ -1,7 +1,6 @@ #' Internal function to run metadata checks #' This is useful for testing, particularly in testing `galah_select()` #' called by `collapse()` -#' @importFrom utils URLdecode #' @noRd #' @keywords Internal parse_checks <- function(.query){ @@ -21,7 +20,7 @@ parse_checks <- function(.query){ check_profiles() } if(.query$type == "data/distributions" & !is.null(.query[["metadata/distributions"]])){ - .query$url <- tibble(url = glue(URLdecode(.query$url), + .query$url <- tibble(url = glue(utils::URLdecode(.query$url), id = .query[["metadata/distributions"]]$id)) } .query <- remove_metadata(.query) diff --git a/R/read_zip.R b/R/read_zip.R new file mode 100644 index 00000000..baa0f3f8 --- /dev/null +++ b/R/read_zip.R @@ -0,0 +1,106 @@ +#' Read downloaded data from a zip file +#' +#' @description +#' `r lifecycle::badge("experimental")` +#' +#' Living atlases supply data downloads as zip files. This function reads these +#' data efficiently, i.e. without unzipping them first, using the `readr` +#' package. Although this function has been part of galah for some time, it was +#' previously internal to [atlas_occurrences()]. It has been exported now to +#' support easy re-importing of downloaded files, without the need to re-run +#' a query. +#' @param file (character) A file name. Must be a length-1 character ending in +#' `.zip`. +#' @examples \dontrun{ +#' # set a working directory +#' galah_config(directory = "data-raw", +#' email = "an-email-address@email.com") +#' +#' # download some data +#' galah_call() |> +#' identify("Heleioporus") |> +#' filter(year == 2022) |> +#' collect(file = "burrowing_frog_data.zip") +#' +#' # load data from file +#' x <- read_zip("./data-raw/burrowing_frog_data.zip") +#' } +#' @export +read_zip <- function(file){ + # basic checks + if(missing(file)){ + rlang::abort("`file` is missing, with no default") + } + if(!is.character(file) | length(file) > 1){ + rlang::abort("Argument `file` should be a length-1 character") + } + if(!file.exists(file)){ + rlang::abort("`.zip` file not found") + } + if(!grepl(".zip$", file)){ + rlang::abort("`file` should end in `.zip`") + } + # get names of files stored in .zip + all_files <- utils::unzip(file, list = TRUE)$Name + # zip files contain a lot of metadata that `galah` does not import + # import only those files that meet our criteria for 'data' + if(is_gbif()){ + available_files <- all_files[grepl(".csv$", all_files)] + result <- unz(description = file, # require lapply? + filename = available_files) |> + readr::read_tsv(col_types = cols()) |> + suppressWarnings() + # Note: DOIs for GBIF are stored in `compute()` stage, not in the zip file + }else{ + available_files <- all_files[grepl(".csv$", all_files) & + grepl("^data|records", all_files)] + result <- lapply(available_files, + function(a, x){ + # create connection to a specific file within zip + conn <- unz(description = x, + filename = a, + open = "rb") + out <- readr::read_csv(conn, + col_types = readr::cols()) |> + suppressWarnings() + close(conn) + return(out) + }, x = file) |> + dplyr::bind_rows() + # # add doi when mint_doi = TRUE + if(any(all_files == "doi.txt")){ + conn <- unz(description = file, + filename = "doi.txt", + open = "rb") + attr(result, "doi") <- readr::read_file(conn) |> + sub("\\n$", "", x = _) + close(conn) + } + # look for citation in README.html + # BUT first check whether other atlases do this + if(any(all_files == "README.html")){ + conn <- unz(description = file, + filename = "README.html", + open = "rb") + readme <- xml2::read_html(conn) |> + xml2::as_list() |> + unlist() + close(conn) + cite_check <- grepl("cite", names(readme)) + if(any(cite_check)){ + attr(result, "citation") <- readme[cite_check] |> + glue_collapse(sep = "") + } + } + } + # add formatted date + attr(result, "modified_date") <- file.info(file)$mtime |> + format("%e %B %Y") |> + trimws() + # exit safely + if(is.null(result)){ + rlang::abort("No data loaded") + }else{ + result + } +} diff --git a/R/search_all.R b/R/search_all.R index 0290bac3..1cc5c203 100644 --- a/R/search_all.R +++ b/R/search_all.R @@ -86,7 +86,6 @@ #' collect() |> #' dplyr::filter(grepl("date", id)) #' } -#' @importFrom utils adist #' @importFrom rlang as_name #' @importFrom rlang .data #' @export @@ -162,7 +161,7 @@ search_text_cols <- function(df, query){ # order search_all() results if ("id" %in% colnames(result)) { - similarity <- adist(result$id, query)[, 1] # similarity of results to query + similarity <- utils::adist(result$id, query)[, 1] # similarity of results to query result <- result[order(similarity), ] # reorder results } diff --git a/R/url_lookup.R b/R/url_lookup.R index f7681dc2..5dfe28cb 100644 --- a/R/url_lookup.R +++ b/R/url_lookup.R @@ -15,7 +15,6 @@ #' @importFrom glue glue #' @importFrom potions pour #' @importFrom rlang abort -#' @importFrom utils URLencode #' @noRd #' @keywords internal url_lookup <- function(type, @@ -37,9 +36,9 @@ url_lookup <- function(type, if(length(dots) > 0){ glue_data(dots, url_string) |> as.character() |> - URLencode() + utils::URLencode() }else{ - url_string |> URLencode() + url_string |> utils::URLencode() } }else{ if(quiet){ diff --git a/man/atlas_.Rd b/man/atlas_.Rd index 3dd13c84..7136df4e 100644 --- a/man/atlas_.Rd +++ b/man/atlas_.Rd @@ -107,15 +107,15 @@ it will have columns specified by \code{\link[=group_by.data_request]{group_by()}}. } \description{ -An alternative to using \code{collect()} at the end of a query pipe is to -call a function with the \code{atlas_} prefix. These solutions are basically -synonymous, but \code{atlas_} functions differ in two ways: +An alternative to using \code{\link[=collect.data_request]{collect()}} at the +end of a query pipe is to call a function with the \code{atlas_} prefix. These +solutions are basically synonymous, but \code{atlas_} functions differ in two ways: \itemize{ \item They have the ability to accept \code{filter}, \code{select} etc as arguments, -rather than within a pipe, but \strong{only} when using the \code{galah_} forms of -those functions (e.g. \code{galah_filter()}). +rather than within a pipe; but \strong{only} when using the \code{galah_} forms of +those functions (e.g. \code{\link[=galah_filter]{galah_filter()}}). \item \code{atlas_} functions do not require you to specify the \code{method} or \code{type} -arguments to \code{galah_call()}, as they are more specific in what data are +arguments to \code{\link[=galah_call]{galah_call()}}, as they are more specific in what data are being requested. } } @@ -164,7 +164,7 @@ galah_call() |> # Get a list of species within genus "Heleioporus" # (every row is a species with associated taxonomic data) galah_call() |> - galah_identify("Heleioporus") |> + identify("Heleioporus") |> atlas_species() # Download Regent Honeyeater records with multimedia attached @@ -176,8 +176,8 @@ galah_call() |> # Get a taxonomic tree of *Chordata* down to the class level galah_call() |> - galah_identify("chordata") |> - galah_filter(rank == class) |> + identify("chordata") |> + filter(rank == class) |> atlas_taxonomy() } } diff --git a/man/read_zip.Rd b/man/read_zip.Rd new file mode 100644 index 00000000..3f06a2fd --- /dev/null +++ b/man/read_zip.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/read_zip.R +\name{read_zip} +\alias{read_zip} +\title{Read downloaded data from a zip file} +\usage{ +read_zip(file) +} +\arguments{ +\item{file}{(character) A file name. Must be a length-1 character ending in +\code{.zip}.} +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Living atlases supply data downloads as zip files. This function read these +data efficiently, i.e. without unzipping them first, using the \code{readr} +package. Although this has been part of galah for some time, it was +previously internal to \code{\link[=atlas_occurrences]{atlas_occurrences()}}. It has been exported now to +support easy re-importing of downloaded files, without the need to re-run +a query. +} +\examples{ +\dontrun{ +# set a working directory +galah_config(directory = "data-raw", + email = "an-email-address@email.com") + +# download some data +galah_call() |> + identify("Heleioporus") |> + filter(year == 2022) |> + collect(file = "burrowing_frog_data.zip") + +# load data from file +x <- read_zip("./data-raw/burrowing_frog_data.zip") +} +} From 28ecfb34cbb1b6d71a66ae5e4a8400c13e0b361a Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Wed, 5 Feb 2025 16:19:18 +1100 Subject: [PATCH 22/27] Update new vignette to pre-render; add `read_zip()` example --- vignettes/download-data-reproducibly.Rmd | 129 +++++++++++--- vignettes/download-data-reproducibly.Rmd.orig | 161 ++++++++++++++++++ 2 files changed, 264 insertions(+), 26 deletions(-) create mode 100644 vignettes/download-data-reproducibly.Rmd.orig diff --git a/vignettes/download-data-reproducibly.Rmd b/vignettes/download-data-reproducibly.Rmd index 8dc2dae4..52e40478 100644 --- a/vignettes/download-data-reproducibly.Rmd +++ b/vignettes/download-data-reproducibly.Rmd @@ -36,6 +36,13 @@ won't necessarily return the same data every time we run the same query. How can we preserve the result returned by a query if the query always changes? +We recommend you take two steps to ensure your data are stable: + +- download data to a specified directory and file name so you can find and re-use it later +- request a DOI with your download so you can download it again if needed (and cite it correctly!) + +In practice, you can enact both steps in the same workflow. + ## Generate a data DOI To make data downloads reproducible, galah allows us to mint a unique @@ -53,39 +60,42 @@ In galah, you can also add a DOI to your download query. You just need to add `mint_doi = TRUE` to `atlas_occurrences()`. A unique DOI will be assigned to the resulting object once the query is run. -Please note that this functionality is currently supported only for +Please note that DOI generation and reload is currently supported only for queries to the Atlas of Living Australia. GBIF provides a DOI with every download, but at the time of writing, they don't provide an API to use -that DOI to download the dataset a second time. +that DOI to download the dataset a second time. Other atlases don't support +DOIs at all (yet). -```{r} -#| eval: false -#| echo: true + +``` r library(galah) -galah_config(email = "your-email-here") +galah_config(email = "your-email-here", + directory = "ALA_downloads") # download bandicoot occurrence records from 2003 occs <- galah_call() |> identify("perameles") |> filter(year == 2003) |> - atlas_occurrences(mint_doi = TRUE) # add DOI + atlas_occurrences(mint_doi = TRUE, # add DOI + file = "bandicoots_2003_data.zip" # specify download filename + ) occs |> print(n = 5) ``` -```{r} -#| eval: true -#| echo: false -#| warning: false -#| message: false -library(galah) -galah_config(email = "dax.kellie@csiro.au", verbose = FALSE) -occs <- galah_call() |> - identify("perameles") |> - filter(year == 2003) |> - atlas_occurrences(mint_doi = TRUE) # add DOI -occs |> print(n = 5) + +``` +## # A tibble: 201 × 8 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate +## +## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 +## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 +## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 +## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 +## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## # ℹ 196 more rows +## # ℹ 2 more variables: occurrenceStatus , dataResourceName ``` galah preserves lots of information in an object's attributes, many @@ -93,25 +103,77 @@ which are used to construct the API query sent to the specified Living Atlas. We can view new DOI assigned to `occs` by checking its attributes. -```{r} + +``` r attributes(occs)$doi ``` -We now have a persistent link to this data download. If we paste this -DOI into your browser, we can see additional information about our -query, including a breakdown of data providers and licensing. +``` +## [1] "https://doi.org/10.26197/ala.608d7f78-00f8-4f98-b204-5c3c386b3225" +``` + +We can also view information on how to cite this dataset: -```{r atlas-support, echo = FALSE, out.width = "100%"} -knitr::include_graphics('../man/figures/doi-web-example.png') + +``` r +atlas_citation(occs) +``` + +``` +## The citation for this dataset is: +## +## Atlas of Living Australia (5 February 2025) Occurrence download https://doi.org/10.26197/ala.608d7f78-00f8-4f98-b204-5c3c386b3225 +## ``` +``` +## +## Please consider citing R & galah, in addition to your dataset: +## +## R Core Team (2024). _R: A Language and Environment for Statistical Computing_. R Foundation for Statistical Computing, Vienna, Austria. . +## +## Westgate M, Stevenson M, Kellie D, Newman P (2025). _galah: Biodiversity Data from the GBIF Node Network_. . +``` + +If you need to reload this data locally, you can do that by simply calling +`read_zip()`: + + +``` r +read_zip("./ALA_downloads/bandicoots_2003_data.zip") |> + print(n = 5) +``` + +``` +## # A tibble: 201 × 8 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate +## +## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 +## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 +## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 +## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 +## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## # ℹ 196 more rows +## # ℹ 2 more variables: occurrenceStatus , dataResourceName +``` + +More importantly, though, we now have a persistent link to this data download. +If we paste this DOI into your browser, we can see additional information about +our query, including a breakdown of data providers and licensing. + +
+plot of chunk atlas-support +

plot of chunk atlas-support

+
+ ## Download data using a DOI To use a DOI to return the results of a query again, we'll use `galah_filter()`. We can specify that we would like to filter our results to only the records returned by our DOI. -```{r} + +``` r occs_again <- galah_call() |> filter(doi == attributes(occs)$doi) |> # filter by doi atlas_occurrences() @@ -119,7 +181,22 @@ occs_again <- galah_call() |> occs_again |> print(n = 5) ``` +``` +## # A tibble: 201 × 8 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate +## +## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 +## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 +## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 +## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 +## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## # ℹ 196 more rows +## # ℹ 2 more variables: occurrenceStatus , dataResourceName +``` + Our query reproduces the the same records as our original query! If you would like other examples, we use this DOI method to reproducibly download data throughout the [*Cleaning Biodiversity Data in R*](https://cleaning-data-r.ala.org.au/data-in-this-book.html) book. + + diff --git a/vignettes/download-data-reproducibly.Rmd.orig b/vignettes/download-data-reproducibly.Rmd.orig new file mode 100644 index 00000000..dbbe5d6c --- /dev/null +++ b/vignettes/download-data-reproducibly.Rmd.orig @@ -0,0 +1,161 @@ +--- +title: "Download data reproducibly" +author: "Dax Kellie & Martin Westgate" +date: '2023-10-13' +output: + rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Download data reproducibly} + %\VignetteEncoding{UTF-8} + %\VignetteEngine{knitr::rmarkdown} +resource_files: + - '../man/figures/doi-web-example.png' +editor_options: + markdown: + wrap: 72 +--- + +Reproducible data downloads are important for a project's long-term use +and longevity. Below we briefly discuss why galah queries aren't +reproducible by default, how to make a data download reproducible, and +how to download the same data again in future. + +## The trade-off of new data + +By default, galah downloads data directly from each Living Atlas API. +This default means that galah automatically downloads the most +up-to-date data available (yay!). + +Living Atlases, however, are constantly ingesting more data. These new +data may be from within the last week, the last year, or the last +hundred years, depending on the source. As a result, even if we run the +same query, the data we download today may return a different result from +the data we downloaded yesterday! Frequent data ingestion means that +although galah is useful because it downloads the latest data, galah +won't necessarily return the same data every time we run the same query. +How can we preserve the result returned by a query if the query always +changes? + +We recommend you take two steps to ensure your data are stable: + +- download data to a specified directory and file name so you can find and re-use it later +- request a DOI with your download so you can download it again if needed (and cite it correctly!) + +In practice, you can enact both steps in the same workflow. + +## Generate a data DOI + +To make data downloads reproducible, galah allows us to mint a unique +*DOI* (Digital Object Identifier) for a specific query and its +subsequent result. + +A DOI works very similarly to a url, in that it holds content that is +accessed using a specific link. However, a url can "break" if the url is +renamed or the content once found under a certain url is moved to a new +url. In contrast, a DOI is persistent and will always direct a user to +the same content, even if the url changes. This persistence is why DOIs +are valuable for long-term reproducibility of content. + +In galah, you can also add a DOI to your download query. You just need +to add `mint_doi = TRUE` to `atlas_occurrences()`. A unique DOI will be +assigned to the resulting object once the query is run. + +Please note that DOI generation and reload is currently supported only for +queries to the Atlas of Living Australia. GBIF provides a DOI with every +download, but at the time of writing, they don't provide an API to use +that DOI to download the dataset a second time. Other atlases don't support +DOIs at all (yet). + + +```{r} +#| eval: false +#| echo: true +library(galah) +galah_config(email = "your-email-here", + directory = "ALA_downloads") + +# download bandicoot occurrence records from 2003 +occs <- galah_call() |> + identify("perameles") |> + filter(year == 2003) |> + atlas_occurrences(mint_doi = TRUE, # add DOI + file = "bandicoots_2003_data.zip" # specify download filename + ) + +occs |> print(n = 5) +``` + +```{r} +#| eval: true +#| echo: false +#| warning: false +#| message: false +library(galah) +galah_config(email = "dax.kellie@csiro.au", + directory = "ALA_downloads", + verbose = FALSE) +occs <- galah_call() |> + identify("perameles") |> + filter(year == 2003) |> + atlas_occurrences(mint_doi = TRUE, + file = "bandicoots_2003_data.zip") +occs |> print(n = 5) +``` + +galah preserves lots of information in an object's attributes, many +which are used to construct the API query sent to the specified Living +Atlas. We can view new DOI assigned to `occs` by checking its +attributes. + +```{r} +attributes(occs)$doi +``` + +We can also view information on how to cite this dataset: + +```{r} +atlas_citation(occs) +``` + +If you need to reload this data locally, you can do that by simply calling +`read_zip()`: + +```{r} +read_zip("./ALA_downloads/bandicoots_2003_data.zip") |> + print(n = 5) +``` + +More importantly, though, we now have a persistent link to this data download. +If we paste this DOI into your browser, we can see additional information about +our query, including a breakdown of data providers and licensing. + +```{r atlas-support, echo = FALSE, out.width = "100%"} +knitr::include_graphics('../man/figures/doi-web-example.png') +``` + +## Download data using a DOI + +To use a DOI to return the results of a query again, we'll use +`galah_filter()`. We can specify that we would like to filter our +results to only the records returned by our DOI. + +```{r} +occs_again <- galah_call() |> + filter(doi == attributes(occs)$doi) |> # filter by doi + atlas_occurrences() + +occs_again |> print(n = 5) +``` + +Our query reproduces the the same records as our original query! + +If you would like other examples, we use this DOI method to +reproducibly download data throughout the [*Cleaning Biodiversity Data in R*](https://cleaning-data-r.ala.org.au/data-in-this-book.html) book. + +```{r} +#| eval: true +#| echo: false +#| warning: false +#| message: false +unlink("ALA_downloads", recursive = TRUE) +``` \ No newline at end of file From 0d7e8052aa0785dc493e013a09d880031473a6f0 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Wed, 5 Feb 2025 16:24:04 +1100 Subject: [PATCH 23/27] Minor fixes to pkgdown and help to ensure site builds correctly --- R/galah-package.R | 1 + _pkgdown.yml | 1 + man/galah.Rd | 1 + man/read_zip.Rd | 4 ++-- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/R/galah-package.R b/R/galah-package.R index 1f47022e..68e2a553 100644 --- a/R/galah-package.R +++ b/R/galah-package.R @@ -52,6 +52,7 @@ #' **Miscellaneous functions** #' #' * [atlas_citation()] Get a citation for a dataset +#' * [read_zip()] To read data from an earlier download #' * \code{\link[=print.data_request]{print()}} Print functions for galah objects #' #' @section Terminology: diff --git a/_pkgdown.yml b/_pkgdown.yml index a0503aa4..16dc8071 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -95,5 +95,6 @@ reference: contents: - taxonomic_searches - print_galah_objects + - read_zip - atlas_citation - title: internal diff --git a/man/galah.Rd b/man/galah.Rd index 17efead7..15076889 100644 --- a/man/galah.Rd +++ b/man/galah.Rd @@ -60,6 +60,7 @@ For those outside Australia, 'galah' is the common name of \strong{Miscellaneous functions} \itemize{ \item \code{\link[=atlas_citation]{atlas_citation()}} Get a citation for a dataset +\item \code{\link[=read_zip]{read_zip()}} To read data from an earlier download \item \code{\link[=print.data_request]{print()}} Print functions for galah objects } } diff --git a/man/read_zip.Rd b/man/read_zip.Rd index 3f06a2fd..3d0a206a 100644 --- a/man/read_zip.Rd +++ b/man/read_zip.Rd @@ -13,9 +13,9 @@ read_zip(file) \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -Living atlases supply data downloads as zip files. This function read these +Living atlases supply data downloads as zip files. This function reads these data efficiently, i.e. without unzipping them first, using the \code{readr} -package. Although this has been part of galah for some time, it was +package. Although this function has been part of galah for some time, it was previously internal to \code{\link[=atlas_occurrences]{atlas_occurrences()}}. It has been exported now to support easy re-importing of downloaded files, without the need to re-run a query. From 5248edf917514615de6975fea09de9dd37588746 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Thu, 6 Feb 2025 11:24:18 +1100 Subject: [PATCH 24/27] fix missing pkg link for `readr::cols()` --- R/read_zip.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/read_zip.R b/R/read_zip.R index baa0f3f8..e338234b 100644 --- a/R/read_zip.R +++ b/R/read_zip.R @@ -48,7 +48,7 @@ read_zip <- function(file){ available_files <- all_files[grepl(".csv$", all_files)] result <- unz(description = file, # require lapply? filename = available_files) |> - readr::read_tsv(col_types = cols()) |> + readr::read_tsv(col_types = readr::cols()) |> suppressWarnings() # Note: DOIs for GBIF are stored in `compute()` stage, not in the zip file }else{ From b05cd2f91cb7708ff559322653456143317fb7e5 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Fri, 7 Feb 2025 12:02:31 +1100 Subject: [PATCH 25/27] updates to atlas_citation() - improved printing and clarity - updated help and tests - updates example in reproducibility vignette --- R/atlas_citation.R | 119 +++++++++++------- inst/CITATION | 2 +- man/atlas_citation.Rd | 24 ++-- tests/testthat/test-atlas_citation.R | 43 +++++-- vignettes/download-data-reproducibly.Rmd | 84 +++++++------ vignettes/download-data-reproducibly.Rmd.orig | 12 +- 6 files changed, 180 insertions(+), 104 deletions(-) diff --git a/R/atlas_citation.R b/R/atlas_citation.R index a22447f9..be45f5aa 100644 --- a/R/atlas_citation.R +++ b/R/atlas_citation.R @@ -1,74 +1,109 @@ #' Generate a citation for occurrence data #' -#' If a `data.frame` was generated using [atlas_occurrences()], -#' and the `mint_doi` argument was set to `TRUE`, the DOI associated -#' with that dataset is appended to the resulting `data.frame` as an -#' attribute. This function simply formats that DOI as a citation that can be -#' included in a scientific publication. Please also consider citing this -#' package, using the information in `citation("galah")`. -#' @param data data.frame: occurrence data generated by -#' [atlas_occurrences()] -#' @return A string containing the citation for that dataset. +#' If a `tibble` containing occurrences was generated using galah (either via +#' \code{\link[=collect.data_request]{collect()}} or [atlas_occurrences()]), it +#' will usually contain associated metadata stored in `attributes()` that can be +#' used to build a citation for that dataset. This function simply extracts that +#' information, formats it, then both invisibly returns the formatted citation +#' and prints it to the console. +#' @param data A `tibble` generated by [atlas_occurrences()] or similar +#' @return Invisibly returns a string containing the citation for that dataset. +#' Primarily called for the side-effect of printing this string to the console. #' @examples \dontrun{ -#' atlas_citation(doi) +#' x <- galah_call() |> +#' identify("Heleioporus") |> +#' filter(year == 2022) |> +#' collect() +#' atlas_citation(x) #' } #' @export atlas_citation <- function(data) { # get basic information from file modified_date <- attributes(data)$modified_date + if(is.null(modified_date)){ + modified_date <- Sys.Date() |> + format("%e %B %Y") |> + trimws() + } doi <- attributes(data)$doi citation <- attributes(data)$citation - search_url <- attributes(data)$search_url + # search_url <- attributes(data)$search_url # get existing citation info r_citation <- citation() |> print(style = 'text') |> utils::capture.output() |> glue_collapse(sep = " ") - galah_citation <- citation(package = "galah") |> - print(style = 'text') |> - utils::capture.output() |> + galah_citation <- c("Westgate M, Kellie D, Stevenson M & Newman P (2025):", + "_galah: Biodiversity Data from the GBIF Node Network_.", + "R package version 2.1.1.", + "doi: 10.32614/CRAN.package.galah") |> glue_collapse(sep = " ") + # ask users to cite galah and R + suffix_text <- glue(" + + Please consider citing R & galah, in addition to your dataset: + + {r_citation} + + {galah_citation}") + + # set case when DOI is missing - if (is.null(doi)) { - if(!is.null(citation)){ - glue(" + if(!is.null(doi)) { + # ALA + if(grepl("10.26197/ala.", doi)){ + result <- glue(" The citation for this dataset is: - {citation} + Atlas of Living Australia ({modified_date}) Occurrence download {doi} + + {suffix_text} + + ") + cli::cli_text(result) + invisible(result) + # GBIF + }else if(grepl("10.15468/dl.", doi)){ + result <- glue(" + The citation for this dataset is: + + GBIF.org ({modified_date}) GBIF Occurrence Download {doi} + + {suffix_text} - ") |> - cat() + ") + cli::cli_text(result) + invisible(result) + # Unknown }else{ bullets <- c( - "This dataset does not have any citation information attached.", + "The supplied DOI was not recognized.", i = "Please consider checking the atlas in question for their citation guidelines" ) - warn(bullets, call = caller_env()) + cli::cli_warn(bullets) + invisible(bullets) } - }else{ # i.e. if DOI present - if(grepl("10.26197/ala.", doi)){ - org_text <- "Atlas of Living Australia" - description <- "Occurrence download" - }else{ - org_text <- "GBIF.org" - description <- "GBIF Occurrence Download" - } - - glue(" + }else{ + if(!is.null(citation)){ + result <- glue(" The citation for this dataset is: - {org_text} ({modified_date}) {description} {doi} - - ") |> cat() + {citation} + + {suffix_text} + ") + cli::cli_text(result) + invisible(result) + }else{ + bullets <- c( + "This dataset does not have any citation information attached.", + i = "Please consider checking the atlas in question for their citation guidelines" + ) + cli::cli_warn(bullets) + invisible(bullets) + } } - glue(" - - Please consider citing R & galah, in addition to your dataset: - - {r_citation} - - {galah_citation}") } diff --git a/inst/CITATION b/inst/CITATION index 7c3d7bb7..de1ad268 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -6,8 +6,8 @@ citHeader("To cite galah in publications use:") bibentry(bibtype = "Manual", "title" = "galah: Biodiversity Data from the GBIF Node Network", author = c(person("Martin", "Westgate"), - person("Matilda", "Stevenson"), person("Dax", "Kellie"), + person("Matilda", "Stevenson"), person("Peggy", "Newman")), year = year, note = note, diff --git a/man/atlas_citation.Rd b/man/atlas_citation.Rd index 29a3877a..53de92f4 100644 --- a/man/atlas_citation.Rd +++ b/man/atlas_citation.Rd @@ -7,22 +7,26 @@ atlas_citation(data) } \arguments{ -\item{data}{data.frame: occurrence data generated by -\code{\link[=atlas_occurrences]{atlas_occurrences()}}} +\item{data}{A \code{tibble} generated by \code{\link[=atlas_occurrences]{atlas_occurrences()}} or similar} } \value{ -A string containing the citation for that dataset. +Invisibly returns a string containing the citation for that dataset. +Primarily called for the side-effect of printing this string to the console. } \description{ -If a \code{data.frame} was generated using \code{\link[=atlas_occurrences]{atlas_occurrences()}}, -and the \code{mint_doi} argument was set to \code{TRUE}, the DOI associated -with that dataset is appended to the resulting \code{data.frame} as an -attribute. This function simply formats that DOI as a citation that can be -included in a scientific publication. Please also consider citing this -package, using the information in \code{citation("galah")}. +If a \code{tibble} containing occurrences was generated using galah (either via +\code{\link[=collect.data_request]{collect()}} or \code{\link[=atlas_occurrences]{atlas_occurrences()}}), it +will usually contain associated metadata stored in \code{attributes()} that can be +used to build a citation for that dataset. This function simply extracts that +information, formats it, then both invisibly returns the formatted citation +and prints it to the console. } \examples{ \dontrun{ -atlas_citation(doi) +x <- galah_call() |> + identify("Heleioporus") |> + filter(year == 2022) |> + collect() +atlas_citation(x) } } diff --git a/tests/testthat/test-atlas_citation.R b/tests/testthat/test-atlas_citation.R index 286637eb..c0811451 100644 --- a/tests/testthat/test-atlas_citation.R +++ b/tests/testthat/test-atlas_citation.R @@ -1,24 +1,53 @@ test_that("atlas_citation generates DOI for dataset with DOI", { df <- data.frame() attr(df, "doi") <- "test-doi" - expect_match(atlas_citation(df), "test-doi") + x <- atlas_citation(df) |> + suppressWarnings() + expect_equal(x, + c("The supplied DOI was not recognized.", + i = "Please consider checking the atlas in question for their citation guidelines")) }) test_that("atlas_citation returns an error when no DOI exists", { data <- data.frame() - expect_warning(atlas_citation(data)) + atlas_citation(data) |> + expect_warning() + x <- atlas_citation(data) |> + suppressWarnings() + expect_equal(x, + c( + "This dataset does not have any citation information attached.", + i = "Please consider checking the atlas in question for their citation guidelines" + )) }) test_that("atlas_citation attributes ALA DOIs correctly", { df <- data.frame() attr(df, "doi") <- "https://doi.org/10.26197/ala.68d1695a-83dd-45bf-88a5-7b65e7fc1553" - citation <- atlas_citation(df) - expect_true(grepl("^Atlas of Living Australia", citation)) + citation <- atlas_citation(df) |> + suppressMessages() + expect_true(grepl("Atlas of Living Australia", citation)) }) test_that("atlas_citation attributes GBIF DOIs correctly", { df <- data.frame() - attr(df, "doi") <- "https://doi.org/10.15468/dl.2q87rx" - citation <- atlas_citation(df) - expect_true(grepl("^GBIF.org", citation)) + attr(df, "doi") <- "10.15468/dl.randomstring" + citation <- atlas_citation(df) |> + suppressMessages() + expect_true(grepl("GBIF Occurrence Download", citation)) +}) + +test_that("atlas_citation works on a real download", { + skip_if_offline(); skip_on_ci() + galah_config(email = "ala4r@ala.org.au") + x <- galah_call() |> + identify("Heleioporus") |> + filter(year == 2022) |> + collect() + text_out <- atlas_citation(x) |> + suppressMessages() + grepl("^The citation for this dataset is:", text_out) |> + expect_true() + grepl("Please consider citing R & galah", text_out) |> + expect_true() }) \ No newline at end of file diff --git a/vignettes/download-data-reproducibly.Rmd b/vignettes/download-data-reproducibly.Rmd index 52e40478..a4e6d81e 100644 --- a/vignettes/download-data-reproducibly.Rmd +++ b/vignettes/download-data-reproducibly.Rmd @@ -1,7 +1,7 @@ --- title: "Download data reproducibly" author: "Dax Kellie & Martin Westgate" -date: '2023-10-13' +date: '2025-02-07' output: rmarkdown::html_vignette vignette: > @@ -38,10 +38,12 @@ changes? We recommend you take two steps to ensure your data are stable: -- download data to a specified directory and file name so you can find and re-use it later -- request a DOI with your download so you can download it again if needed (and cite it correctly!) +- download data to a specified location so you can find and re-use it later +- request a DOI with your download so you can request it again if needed (and cite it correctly!) -In practice, you can enact both steps in the same workflow. +These steps are independent, so you can choose whether to enact +none, one, or both of them in the same workflow. We will show you how to do +both. ## Generate a data DOI @@ -78,7 +80,7 @@ occs <- galah_call() |> identify("perameles") |> filter(year == 2003) |> atlas_occurrences(mint_doi = TRUE, # add DOI - file = "bandicoots_2003_data.zip" # specify download filename + file = "bandicoots_2003_data.zip" # specify filename ) occs |> print(n = 5) @@ -87,15 +89,16 @@ occs |> print(n = 5) ``` ## # A tibble: 201 × 8 -## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate -## -## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 -## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 -## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 -## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 -## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude +## +## 1 00300a96-e… Perameles nas… https://biodi… -33.7 151. +## 2 0134472a-2… Perameles nas… https://biodi… -34.7 151. +## 3 01e7b99e-9… Perameles gun… https://biodi… -42.9 147. +## 4 029c49f0-b… Perameles nas… https://biodi… -35.1 151. +## 5 0345dbe1-9… Perameles nas… https://biodi… -35.5 150. ## # ℹ 196 more rows -## # ℹ 2 more variables: occurrenceStatus , dataResourceName +## # ℹ 3 more variables: eventDate , occurrenceStatus , +## # dataResourceName ``` galah preserves lots of information in an object's attributes, many @@ -109,7 +112,7 @@ attributes(occs)$doi ``` ``` -## [1] "https://doi.org/10.26197/ala.608d7f78-00f8-4f98-b204-5c3c386b3225" +## [1] "https://doi.org/10.26197/ala.7605ea98-e90f-46d4-998c-1b6673c48d34" ``` We can also view information on how to cite this dataset: @@ -122,17 +125,18 @@ atlas_citation(occs) ``` ## The citation for this dataset is: ## -## Atlas of Living Australia (5 February 2025) Occurrence download https://doi.org/10.26197/ala.608d7f78-00f8-4f98-b204-5c3c386b3225 -## -``` - -``` +## Atlas of Living Australia (7 February 2025) Occurrence download +## https://doi.org/10.26197/ala.7605ea98-e90f-46d4-998c-1b6673c48d34 ## ## Please consider citing R & galah, in addition to your dataset: -## -## R Core Team (2024). _R: A Language and Environment for Statistical Computing_. R Foundation for Statistical Computing, Vienna, Austria. . -## -## Westgate M, Stevenson M, Kellie D, Newman P (2025). _galah: Biodiversity Data from the GBIF Node Network_. . +## +## R Core Team (2024). _R: A Language and Environment for Statistical +## Computing_. R Foundation for Statistical Computing, Vienna, Austria. +## . +## +## Westgate M, Kellie D, Stevenson M & Newman P (2025): _galah: Biodiversity +## Data from the GBIF Node Network_. R package version 2.1.1. doi: +## 10.32614/CRAN.package.galah ``` If you need to reload this data locally, you can do that by simply calling @@ -146,15 +150,16 @@ read_zip("./ALA_downloads/bandicoots_2003_data.zip") |> ``` ## # A tibble: 201 × 8 -## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate -## -## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 -## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 -## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 -## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 -## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude +## +## 1 00300a96-e… Perameles nas… https://biodi… -33.7 151. +## 2 0134472a-2… Perameles nas… https://biodi… -34.7 151. +## 3 01e7b99e-9… Perameles gun… https://biodi… -42.9 147. +## 4 029c49f0-b… Perameles nas… https://biodi… -35.1 151. +## 5 0345dbe1-9… Perameles nas… https://biodi… -35.5 150. ## # ℹ 196 more rows -## # ℹ 2 more variables: occurrenceStatus , dataResourceName +## # ℹ 3 more variables: eventDate , occurrenceStatus , +## # dataResourceName ``` More importantly, though, we now have a persistent link to this data download. @@ -183,15 +188,16 @@ occs_again |> print(n = 5) ``` ## # A tibble: 201 × 8 -## recordID scientificName taxonConceptID decimalLatitude decimalLongitude eventDate -## -## 1 00300a96-e91a-4f46-… Perameles nas… https://biodi… -33.7 151. 2003-01-07 00:00:00 -## 2 0134472a-2598-4a7a-… Perameles nas… https://biodi… -34.7 151. 2003-03-05 00:00:00 -## 3 01e7b99e-9c24-44b9-… Perameles gun… https://biodi… -42.9 147. 2003-08-24 00:00:00 -## 4 029c49f0-ba80-402b-… Perameles nas… https://biodi… -35.1 151. 2003-09-04 00:00:00 -## 5 0345dbe1-9e61-437a-… Perameles nas… https://biodi… -35.5 150. 2003-09-01 00:00:00 +## recordID scientificName taxonConceptID decimalLatitude decimalLongitude +## +## 1 00300a96-e… Perameles nas… https://biodi… -33.7 151. +## 2 0134472a-2… Perameles nas… https://biodi… -34.7 151. +## 3 01e7b99e-9… Perameles gun… https://biodi… -42.9 147. +## 4 029c49f0-b… Perameles nas… https://biodi… -35.1 151. +## 5 0345dbe1-9… Perameles nas… https://biodi… -35.5 150. ## # ℹ 196 more rows -## # ℹ 2 more variables: occurrenceStatus , dataResourceName +## # ℹ 3 more variables: eventDate , occurrenceStatus , +## # dataResourceName ``` Our query reproduces the the same records as our original query! diff --git a/vignettes/download-data-reproducibly.Rmd.orig b/vignettes/download-data-reproducibly.Rmd.orig index dbbe5d6c..342b60be 100644 --- a/vignettes/download-data-reproducibly.Rmd.orig +++ b/vignettes/download-data-reproducibly.Rmd.orig @@ -1,7 +1,7 @@ --- title: "Download data reproducibly" author: "Dax Kellie & Martin Westgate" -date: '2023-10-13' +date: '2025-02-07' output: rmarkdown::html_vignette vignette: > @@ -38,10 +38,12 @@ changes? We recommend you take two steps to ensure your data are stable: -- download data to a specified directory and file name so you can find and re-use it later -- request a DOI with your download so you can download it again if needed (and cite it correctly!) +- download data to a specified location so you can find and re-use it later +- request a DOI with your download so you can request it again if needed (and cite it correctly!) -In practice, you can enact both steps in the same workflow. +These steps are independent, so you can choose whether to enact +none, one, or both of them in the same workflow. We will show you how to do +both. ## Generate a data DOI @@ -79,7 +81,7 @@ occs <- galah_call() |> identify("perameles") |> filter(year == 2003) |> atlas_occurrences(mint_doi = TRUE, # add DOI - file = "bandicoots_2003_data.zip" # specify download filename + file = "bandicoots_2003_data.zip" # specify filename ) occs |> print(n = 5) From 1989612cd309322472f2058fb9c6e90da90c5184 Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Fri, 7 Feb 2025 13:48:39 +1100 Subject: [PATCH 26/27] fix bug identified by `check()` - missing xml2 dependency - missing package definition for `readr::col()` - outdated tests for `atlas_citation()` in unexpected files --- DESCRIPTION | 3 ++- R/collect_species.R | 2 +- tests/testthat/test-atlas_occurrences.R | 2 -- tests/testthat/test-international-GBIF.R | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 04eaaabc..7673b974 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -44,7 +44,8 @@ Imports: tibble, tidyr, tidyselect, - utils + utils, + xml2 Suggests: covr, gt, diff --git a/R/collect_species.R b/R/collect_species.R index 9502ee04..e2cbd533 100644 --- a/R/collect_species.R +++ b/R/collect_species.R @@ -10,7 +10,7 @@ collect_species <- function(.query, file = NULL){ readr::read_csv(.query$file, col_names = get_clean_colnames(.query$file, facet = .query$group_by$name), - col_types = cols(), + col_types = readr::cols(), skip = 1) } } diff --git a/tests/testthat/test-atlas_occurrences.R b/tests/testthat/test-atlas_occurrences.R index 178d63ed..4f38d70a 100644 --- a/tests/testthat/test-atlas_occurrences.R +++ b/tests/testthat/test-atlas_occurrences.R @@ -192,8 +192,6 @@ test_that("`atlas_occurrences()` places DOI in `attr()` correctly", { y <- attr(x, "doi") expect_false(is.null(y)) expect_true(grepl("^https://doi.org/", y)) - citation <- atlas_citation(x) - expect_true(grepl("^Atlas of Living Australia", citation)) rm(x, y) # ditto for atlas_occurrences x <- galah_call() |> diff --git a/tests/testthat/test-international-GBIF.R b/tests/testthat/test-international-GBIF.R index 7e12f3a7..9be5c481 100644 --- a/tests/testthat/test-international-GBIF.R +++ b/tests/testthat/test-international-GBIF.R @@ -365,8 +365,7 @@ test_that("`collapse()` et al. work for GBIF with `type = 'occurrences'`", { expect_gt(ncol(z), 0) expect_true(inherits(z, c("tbl_df", "tbl", "data.frame"))) expect_equal(nrow(z), count$count) - citation <- atlas_citation(z) - expect_true(grepl("^GBIF.org", citation)) + expect_true(!is.null(attributes(z)$doi)) }) galah_config(atlas = "Australia") From a09285f619aa45efec21a2ffa153e7025b73c33d Mon Sep 17 00:00:00 2001 From: Martin Westgate Date: Fri, 7 Feb 2025 14:24:05 +1100 Subject: [PATCH 27/27] update NEWS, fix missing word in vignette --- NEWS.md | 3 +++ vignettes/download-data-reproducibly.Rmd | 2 +- vignettes/download-data-reproducibly.Rmd.orig | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3f0d0d7e..7a55fbaf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,10 @@ # galah 2.1.1 ### Minor improvements +* New vignette to demonstrate methods that support reproducibility +* New function `read_zip()` to reimport downloaded files * Support `group_by()` in occurrence queries to allow facet downloads by any variable (#195, #258) +* Improvements to `atlas_citation()` for improved clarity ### Bug fixes * Improved documentation to use `galah_filter()` to specify a `taxon_concept_id` rather than `galah_identify()` (#245) diff --git a/vignettes/download-data-reproducibly.Rmd b/vignettes/download-data-reproducibly.Rmd index a4e6d81e..40e452d4 100644 --- a/vignettes/download-data-reproducibly.Rmd +++ b/vignettes/download-data-reproducibly.Rmd @@ -103,7 +103,7 @@ occs |> print(n = 5) galah preserves lots of information in an object's attributes, many which are used to construct the API query sent to the specified Living -Atlas. We can view new DOI assigned to `occs` by checking its +Atlas. We can view the new DOI assigned to `occs` by checking its attributes. diff --git a/vignettes/download-data-reproducibly.Rmd.orig b/vignettes/download-data-reproducibly.Rmd.orig index 342b60be..2cbf2024 100644 --- a/vignettes/download-data-reproducibly.Rmd.orig +++ b/vignettes/download-data-reproducibly.Rmd.orig @@ -106,7 +106,7 @@ occs |> print(n = 5) galah preserves lots of information in an object's attributes, many which are used to construct the API query sent to the specified Living -Atlas. We can view new DOI assigned to `occs` by checking its +Atlas. We can view the new DOI assigned to `occs` by checking its attributes. ```{r}