# ==============================================================================
# getters for the opensensemap API.
# the awesome httr library does all the curling, query and response parsing.
# for CSV responses (get_measurements) the readr package is a hidden dependency
# ==============================================================================
default_api = ' https://api.opensensemap.org'
#' Get the default openSenseMap API endpoint
#' @export
#' @return A character string with the HTTP URL of the openSenseMap API
osem_endpoint = function ( ) default_api
#' Check if the given openSenseMap API endpoint is available
#' @param endpoint The API base URL to check, defaulting to \code{\link{osem_endpoint}}
#' @return \code{TRUE} if the API is available, otherwise \code{stop()} is called.
osem_ensure_api_available = function ( endpoint = osem_endpoint ( ) ) {
code = FALSE
try ( {
code = httr :: status_code ( httr :: GET ( endpoint , path = ' stats' ) )
} , silent = TRUE )
if ( code == 200 )
return ( TRUE )
errtext = paste ( ' The API at' , endpoint , ' is currently not available.' )
if ( code != FALSE )
errtext = paste0 ( errtext , ' (HTTP code ' , code , ' )' )
if ( endpoint == default_api )
errtext = c ( errtext , ' If the issue persists, please check back at https://status.sensebox.de/778247404 and notify support@sensebox.de' )
stop ( paste ( errtext , collapse = ' \n ' ) , call. = FALSE )
FALSE
}
get_boxes_ = function ( ... , endpoint ) {
response = osem_get_resource ( endpoint , path = c ( ' boxes' ) , ... )
if ( length ( response ) == 0 ) {
warning ( ' no senseBoxes found for this query' )
return ( osem_as_sensebox ( as.data.frame ( response ) ) )
}
# parse each list element as sensebox & combine them to a single data.frame
boxesList = lapply ( response , parse_senseboxdata )
df = dplyr :: bind_rows ( boxesList )
df $ exposure = df $ exposure %>% as.factor ( )
df $ model = df $ model %>% as.factor ( )
if ( ! is.null ( df $ grouptag ) ) {
df $ grouptag = df $ grouptag %>% as.factor ( )
}
df
}
get_box_ = function ( boxId , endpoint , ... ) {
osem_get_resource ( endpoint , path = c ( ' boxes' , boxId ) , ... , progress = FALSE ) %>%
parse_senseboxdata ( )
}
parse_measurement_csv = function ( resText ) {
# parse the CSV response manually & mute readr
suppressWarnings ( {
result = readr :: read_csv ( resText , col_types = readr :: cols (
# factor as default would raise issues with concatenation of multiple requests
.default = readr :: col_character ( ) ,
createdAt = readr :: col_datetime ( ) ,
value = readr :: col_double ( ) ,
lat = readr :: col_double ( ) ,
lon = readr :: col_double ( ) ,
height = readr :: col_double ( )
) )
} )
osem_as_measurements ( result )
}
get_measurements_ = function ( ... , endpoint ) {
osem_get_resource ( endpoint , c ( ' boxes' , ' data' ) , ... , type = ' text' ) %>%
parse_measurement_csv
}
get_stats_ = function ( endpoint , cache ) {
result = osem_get_resource ( endpoint , path = c ( ' stats' ) , progress = FALSE , cache = cache )
names ( result ) = c ( ' boxes' , ' measurements' , ' measurements_per_minute' )
result
}
#' Get any resource from openSenseMap API, possibly cache the response
#'
#' @param host API host
#' @param path resource URL
#' @param ... All other parameters interpreted as request query parameters
#' @param type Passed to httr; 'parsed' to return an R object from the response, 'text for a raw response
#' @param progress Boolean whether to print download progress information
#' @param cache Optional path to a directory were responses will be cached. If not NA, no requests will be made when a request for the given is already cached.
#' @return Result of a Request to openSenseMap API
#' @noRd
osem_get_resource = function ( host , path , ... , type = ' parsed' , progress = TRUE , cache = NA ) {
query = list ( ... )
if ( ! is.na ( cache ) ) {
filename = osem_cache_filename ( path , query , host ) %>% paste ( cache , ., sep = ' /' )
if ( file.exists ( filename ) )
return ( readRDS ( filename ) )
}
res = osem_request_ ( host , path , query , type , progress )
if ( ! is.na ( cache ) ) saveRDS ( res , filename )
res
}
osem_cache_filename = function ( path , query = list ( ) , host = osem_endpoint ( ) ) {
httr :: modify_url ( url = host , path = path , query = query ) %>%
digest :: digest ( algo = ' sha1' ) %>%
paste ( ' osemcache' , ., ' rds' , sep = ' .' )
}
#' Purge cached responses from the given cache directory
#'
#' @param location A path to the cache directory, defaults to the
#' sessions' \code{tempdir()}
#' @return Boolean whether the deletion was successful
#'
#' @export
#' @examples
#' \dontrun{
#' osem_boxes(cache = tempdir())
#' osem_clear_cache()
#'
#' cachedir = paste(getwd(), 'osemcache', sep = '/')
#' dir.create(file.path(cachedir), showWarnings = FALSE)
#' osem_boxes(cache = cachedir)
#' osem_clear_cache(cachedir)
#' }
osem_clear_cache = function ( location = tempdir ( ) ) {
list.files ( location , pattern = ' osemcache\\..*\\.rds' ) %>%
lapply ( function ( f ) file.remove ( paste ( location , f , sep = ' /' ) ) ) %>%
unlist ( ) %>%
all ( )
}
osem_request_ = function ( host , path , query = list ( ) , type = ' parsed' , progress = TRUE ) {
# stop() if API is not available
osem_ensure_api_available ( host )
progress = if ( progress && ! is_non_interactive ( ) ) httr :: progress ( ) else NULL
res = httr :: GET ( host , progress , path = path , query = query )
if ( httr :: http_error ( res ) ) {
content = httr :: content ( res , ' parsed' , encoding = ' UTF-8' )
stop ( if ( ' message' %in% names ( content ) ) content $ message else httr :: status_code ( res ) )
}
content = httr :: content ( res , type , encoding = ' UTF-8' )
}