2017-08-12 18:19:47 +02:00
# ==============================================================================
# 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
# ==============================================================================
2019-04-26 16:02:38 +02:00
default_api = ' https://api.opensensemap.org'
2018-01-14 21:48:46 +01:00
#' Get the default openSenseMap API endpoint
#' @export
#' @return A character string with the HTTP URL of the openSenseMap API
2019-04-26 16:02:38 +02:00
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
2018-01-14 21:48:46 +01:00
}
2017-08-10 18:01:09 +02:00
get_boxes_ = function ( ... , endpoint ) {
2018-05-25 01:34:47 +02:00
response = osem_get_resource ( endpoint , path = c ( ' boxes' ) , ... )
2017-08-10 18:01:09 +02:00
if ( length ( response ) == 0 ) {
2018-01-15 12:08:43 +01:00
warning ( ' no senseBoxes found for this query' )
2018-01-14 21:48:46 +01:00
return ( osem_as_sensebox ( as.data.frame ( response ) ) )
2017-08-10 18:01:09 +02:00
}
# parse each list element as sensebox & combine them to a single data.frame
boxesList = lapply ( response , parse_senseboxdata )
2017-08-12 15:17:47 +02:00
df = dplyr :: bind_rows ( boxesList )
df $ exposure = df $ exposure %>% as.factor ( )
df $ model = df $ model %>% as.factor ( )
2018-05-07 01:09:57 +02:00
if ( ! is.null ( df $ grouptag ) )
2018-01-14 21:48:46 +01:00
df $ grouptag = df $ grouptag %>% as.factor ( )
2017-08-12 15:17:47 +02:00
df
2017-08-10 18:01:09 +02:00
}
2018-05-25 01:34:47 +02:00
get_box_ = function ( boxId , endpoint , ... ) {
osem_get_resource ( endpoint , path = c ( ' boxes' , boxId ) , ... , progress = FALSE ) %>%
2017-08-10 18:01:09 +02:00
parse_senseboxdata ( )
}
2018-10-18 16:31:30 +02:00
parse_measurement_csv = function ( resText ) {
2017-08-23 14:23:17 +02:00
# parse the CSV response manually & mute readr
suppressWarnings ( {
2018-10-18 16:31:30 +02:00
result = readr :: read_csv ( resText , col_types = readr :: cols (
2017-11-30 17:53:34 +01:00
# factor as default would raise issues with concatenation of multiple requests
.default = readr :: col_character ( ) ,
2017-08-23 14:23:17 +02:00
createdAt = readr :: col_datetime ( ) ,
value = readr :: col_double ( ) ,
lat = readr :: col_double ( ) ,
lon = readr :: col_double ( ) ,
height = readr :: col_double ( )
) )
} )
2017-08-12 15:17:47 +02:00
2017-08-24 21:19:00 +02:00
osem_as_measurements ( result )
2017-08-10 18:01:09 +02:00
}
2018-10-18 16:31:30 +02:00
get_measurements_ = function ( ... , endpoint ) {
osem_get_resource ( endpoint , c ( ' boxes' , ' data' ) , ... , type = ' text' ) %>%
parse_measurement_csv
}
2018-05-25 01:34:47 +02:00
get_stats_ = function ( endpoint , cache ) {
result = osem_get_resource ( endpoint , path = c ( ' stats' ) , progress = FALSE , cache = cache )
2017-08-10 18:01:09 +02:00
names ( result ) = c ( ' boxes' , ' measurements' , ' measurements_per_minute' )
result
}
2017-08-23 14:23:17 +02:00
2018-05-25 01:34:47 +02:00
#' 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 = T , 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 )
2019-04-26 16:02:38 +02:00
if ( ! is.na ( res ) && ! is.na ( cache ) ) saveRDS ( res , filename )
2018-05-25 01:34:47 +02:00
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
#' \donttest{
#' osem_boxes(cache = tempdir())
#' osem_clear_cache()
#'
#' cachedir = paste(getwd(), 'osemcache', sep = '/')
#' 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 ) {
2019-04-26 16:02:38 +02:00
# stop() if API is not available
osem_ensure_api_available ( host )
2018-01-15 14:51:26 +01:00
progress = if ( progress && ! is_non_interactive ( ) ) httr :: progress ( ) else NULL
2018-05-25 01:34:47 +02:00
res = httr :: GET ( host , progress , path = path , query = query )
2017-08-23 14:23:17 +02:00
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' )
}