Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
^[.]?air[.]toml$
^\.vscode$
^jarl\.yml$
^jarl\.toml$
^data-raw$
^AGENTS.md$
^assets$
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ message: 'To cite package "GeoLocatoR" in publications use:'
type: software
license: GPL-3.0-or-later
title: 'GeoLocatoR: R package for GeoLocator Data Package'
version: 1.3.0
version: 1.3.1
identifiers:
- type: url
value: https://geopressure.org/GeoLocatoR/
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: GeoLocatoR
Title: R package for GeoLocator Data Package
Version: 1.3.0
Version: 1.3.1
Authors@R:
c(
person("Raphaël", "Nussbaumer", , "rafnuss@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-8185-1020")),
Expand Down
15 changes: 15 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# GeoLocatoR v1.3.1

## Main

- [Keep the retrieval latitude in `read_soi_gld()`](https://github.com/GeoPressure/GeoLocatoR/commit/315d186). The default column selection (`filter_col = TRUE`) listed `LongitudeRemoved` and `LongitudeAttached` twice and omitted `LatitudeRemoved`, so every retrieval observation created by `read_soi()` had no latitude. Packages created from SOI data with earlier versions should be recreated, or their retrieval latitudes filled. Coordinates recorded as `99999` (unknown in the SOI database) are now read as missing instead of being written as observations.
- [Read dates from the Access file in `read_soi_gld()` on macOS and Linux](https://github.com/GeoPressure/GeoLocatoR/commit/d442338). `mdb-export` wrote them as `MM/DD/YY HH:MM:SS`, which was parsed as `NA`, so every date (attachment, retrieval, RTC, first and last data, delivery) was missing and `read_soi()` created equipment and retrieval observations without a datetime. They are now exported in ISO format. Packages created from an Access file on these systems with earlier versions should be recreated.

## Minor

- [Select the columns given in a character `filter_col`](https://github.com/GeoPressure/GeoLocatoR/commit/315d186) in `read_soi_gld()`, which previously always failed, and accept data frames for `data_file` and `order_file` as documented.
- [Read `AccSamplingRate`, `PrioritySensor` and `PriorityMemory` with their actual types](https://github.com/GeoPressure/GeoLocatoR/commit/315d186) (number, text and percentage) instead of logical, which turned their values into `NA`.
- [Warn when `read_soi()` cannot read the data of a tag](https://github.com/GeoPressure/GeoLocatoR/commit/315d186), naming the tag and the error, instead of silently leaving its measurements out.

**Full Changelog**: <https://github.com/GeoPressure/GeoLocatoR/compare/v1.3.0...v1.3.1>

# GeoLocatoR v1.3.0

## Main
Expand Down
4 changes: 4 additions & 0 deletions R/read_soi.R
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ read_soi <- function(
}
},
error = function(e) {
cli_warn(c(
"!" = "Could not read the data of tag {.val {GDL_ID}} in {.file {directory}}. Its measurements are skipped.",
"x" = conditionMessage(e)
))
list() # Return empty list on error
}
)
Expand Down
58 changes: 43 additions & 15 deletions R/read_soi_gld.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
#' memory. Required when `access_file` is not provided.
#' @param order_file Path to the GDL order file, or a data frame already loaded
#' in memory. Required when `access_file` is not provided.
#' @param filter_col Logical. If `TRUE` (default), returns a predefined subset
#' of columns useful for package creation; if `FALSE`, returns all joined
#' columns.
#' @param filter_col Logical or character vector. If `TRUE` (default), returns
#' a predefined subset of columns useful for package creation; if `FALSE`,
#' returns all joined columns; if a character vector, returns these columns.
#'
#' @return A data frame combining GDL tag rows and order-level information.
#'
Expand All @@ -36,8 +36,11 @@ read_soi_gld <- function(
order_file = NA,
filter_col = TRUE
) {
if (!is.na(access_file)) {
if (!is.na(data_file) || !is.na(order_file)) {
# A file path or a data frame counts as provided
provided <- function(x) is.data.frame(x) || !(length(x) == 1 && is.na(x))

if (provided(access_file)) {
if (provided(data_file) || provided(order_file)) {
cli_warn(c(
"!" = "{.arg access_file} was provided alongside {.arg data_file} or {.arg order_file}.",
">" = "We'll use {.arg access_file}."
Expand All @@ -47,7 +50,7 @@ read_soi_gld <- function(
data_file <- data_order_file[[1]]
order_file <- data_order_file[[2]]
} else {
if (is.na(data_file) || is.na(order_file)) {
if (!provided(data_file) || !provided(order_file)) {
cli_abort(c(
"x" = "Need {.arg access_file} or both {.arg data_file} and {.arg order_file}."
))
Expand All @@ -60,6 +63,20 @@ read_soi_gld <- function(
d <- read_soi_gld_data(data_file)
}

# 99999 encodes an unknown coordinate in the SOI database
d <- mutate(
d,
across(
any_of(c(
"LongitudeAttached",
"LatitudeAttached",
"LongitudeRemoved",
"LatitudeRemoved"
)),
~ dplyr::if_else(.x == 99999, NA_real_, as.numeric(.x))
)
)

if (is.data.frame(order_file)) {
o <- order_file
} else {
Expand Down Expand Up @@ -105,8 +122,7 @@ read_soi_gld <- function(
"LatitudeAttached",
"UTC_Removed",
"LongitudeRemoved",
"LongitudeRemoved",
"LongitudeAttached",
"LatitudeRemoved",

# Information needed for package
"RingNumber",
Expand All @@ -118,7 +134,7 @@ read_soi_gld <- function(
)
}
} else {
gdl <- select(gdl, "filter_col")
gdl <- select(gdl, all_of(filter_col))
}

return(gdl)
Expand Down Expand Up @@ -178,7 +194,7 @@ read_soi_gld_orders <- function(order_file) {
BreastHarnessDiameterHead = readr::col_double(),
BreastHarnessDiameterTail = readr::col_double(),
HarnessDescription = readr::col_character(),
AccSamplingRate = readr::col_logical(),
AccSamplingRate = readr::col_double(),
AccXaxisEnabled = readr::col_logical(),
AccYaxisEnabled = readr::col_logical(),
AccZaxisEnabled = readr::col_logical(),
Expand All @@ -195,7 +211,7 @@ read_soi_gld_orders <- function(order_file) {
TRSchedule = readr::col_character(),
LoggingSchedule = readr::col_character(),
ActiveAfterWakeUp = readr::col_double(),
PrioritySensor = readr::col_logical(),
PrioritySensor = readr::col_character(),
PriorityMemory = readr::col_double()
)
) |>
Expand Down Expand Up @@ -283,8 +299,8 @@ read_soi_gld_data <- function(data_file) {
Frequency = readr::col_double(),
MagCalib = readr::col_character(),
SleepCurrent = readr::col_double(),
PrioritySensor = readr::col_logical(),
PriorityMemory = readr::col_logical(),
PrioritySensor = readr::col_character(),
PriorityMemory = readr::col_double(),
LongitudeRemoved = readr::col_double()
)
)
Expand All @@ -310,9 +326,18 @@ read_soi_gld_access <- function(
))
}

# Exporting needs an Access file and mdb-export or the Access ODBC driver,
# which are not available on CI
# nocov start
if (.Platform$OS.type == "unix") {
# mdb-export writes dates as MM/DD/YY by default, which readr cannot parse
# and turns into NA, so request ISO dates explicitly
date_args <- "-D '%Y-%m-%d' -T '%Y-%m-%d %H:%M:%S'"

# Export data
system(glue::glue("mdb-export '{access_file}' GDL_Data > {data_file}"))
system(glue::glue(
"mdb-export {date_args} '{access_file}' GDL_Data > {data_file}"
))

if (!file.exists(data_file)) {
cli_abort(c(
Expand All @@ -322,7 +347,9 @@ read_soi_gld_access <- function(
}

# Export orders
system(glue::glue("mdb-export '{access_file}' GDL_Orders > {order_file}"))
system(glue::glue(
"mdb-export {date_args} '{access_file}' GDL_Orders > {order_file}"
))
if (!file.exists(order_file)) {
cli_abort(c(
"x" = "The file {.file {order_file}} does not exist.",
Expand All @@ -347,6 +374,7 @@ read_soi_gld_access <- function(
readr::write_csv(DBI::dbReadTable(con, "GDL_Orders"), order_file)
readr::write_csv(DBI::dbReadTable(con, "GDL_Data"), data_file)
}
# nocov end

invisible(c(data_file, order_file))
}
3 changes: 3 additions & 0 deletions jarl.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[lint.undesirable_operator]
# Allow `<<-`, used deliberately to update state from closures; keep `:::` and `->>` banned
operators = ["->>", ":::"]
6 changes: 3 additions & 3 deletions man/read_soi_gld.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions tests/testthat/test-read_soi_gld.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
test_that("read_soi_gld() keeps retrieval coordinates and handles column selection", {
d <- tibble(
OrderName = c("O1", "O1"),
GDL_ID = c("A1", "A2"),
GDL_Type = "GDL3pam",
Species = "Upupa epops",
UTC_Attached = as.POSIXct("2020-05-01", tz = "UTC"),
SiteAttached = "Site",
LongitudeAttached = 7.5,
LatitudeAttached = 46.2,
UTC_Removed = as.POSIXct("2021-05-01", tz = "UTC"),
LongitudeRemoved = c(8.31, 99999),
LatitudeRemoved = c(47.47, 99999),
RingNumber = c("R1", "R2"),
FirmwareVersion = "2.4",
HardwareVersion = "2.4",
TotalWeight = 1.5,
Remarks = NA_character_
)
o <- tibble(
OrderName = "O1",
NumberOrdered = 2L,
NumberDelivered = 2L,
GDL_Type = "GDL3pam",
Species = "Upupa epops",
Client = "SOI",
ResponsibleP3 = "P3",
ClientCategory = "own",
RemarksProduction = NA_character_,
RemarksOrder = NA_character_
)

# Default selection keeps the retrieval latitude, and 99999 is read as unknown
gdl <- read_soi_gld(data_file = d, order_file = o)
expect_true(all(c("LongitudeRemoved", "LatitudeRemoved") %in% names(gdl)))
expect_equal(gdl$LatitudeRemoved, c(47.47, NA))
expect_equal(gdl$LongitudeRemoved, c(8.31, NA))

# A character vector selects these columns
gdl <- read_soi_gld(data_file = d, order_file = o, filter_col = c("GDL_ID", "Client"))
expect_named(gdl, c("GDL_ID", "Client"))

expect_error(read_soi_gld(data_file = d), "Need")
expect_warning(
expect_error(
read_soi_gld(access_file = "missing.accdb", data_file = d),
"does not exist"
),
"access_file"
)

# CSV exports are read with their declared column types; partial exports warn about the
# columns they lack
data_file <- tempfile(fileext = ".csv")
order_file <- tempfile(fileext = ".csv")
on.exit(unlink(c(data_file, order_file)), add = TRUE)
write.csv(mutate(d, PrioritySensor = "LIGHT", PriorityMemory = 20), data_file, row.names = FALSE)
write.csv(mutate(o, AccSamplingRate = 25), order_file, row.names = FALSE)
gdl <- suppressWarnings(
read_soi_gld(data_file = data_file, order_file = order_file, filter_col = FALSE)
)
expect_equal(gdl$LatitudeRemoved, c(47.47, NA))
expect_identical(gdl$PrioritySensor, c("LIGHT", "LIGHT"))
expect_identical(gdl$PriorityMemory, c(20, 20))
expect_identical(suppressWarnings(read_soi_gld_orders(order_file))$AccSamplingRate, 25)
})
Loading