Repository URL to install this package:
|
Version:
0.2.1 ▾
|
#!/usr/bin/env python
# Saildrone API implementation
#
# A very simple implementation of the Saildrone API (generate tokens, push vessels, retrieve timeseries data)
#
# TODO: Check params of get_data and push_vessel if within access scope
# TODO: More sophisticated response and error handling
# TODO: Test time series data and token generation
#
# Copyright (c) 2019 GEOMAR Helmholtz Centre for Ocean Research Kiel
#
# Author: Patrick Leibold
# Last change: 2019-10-20
import os
import requests
import datetime
import logging
class SaildroneAPIError(Exception):
pass
class SaildroneAPI:
"""Very simple and straightforward implementation of Saildrone API"""
# Constants
STATUS_OK = "ok"
DATA_SET_VEHICLE = "vehicle"
DATA_SET_OCEANOGRAPHIC = "oceanographic"
DATA_SET_BIOGEOCHEMICAL = "biogeochemical"
DATA_SET_ATMOSPHERIC = "atmospheric"
DATA_SET_SPY_GLASS = "spyglass"
# Endpoint URLs
__base_url = "https://developer-mission.saildrone.com"
__health_endpoint = "/health"
__auth_endpoint = "/v1/auth"
__access_endpoint = __auth_endpoint + "/access"
__ais_endpoint = "/v1/ais"
__timeseries_endpoint = "/v1/timeseries"
# API specific properties
__token = os.environ.get('SAILDRONE_API_TOKEN')
__headers = {}
__drones = dict()
def __init__(self):
"""Class constructor"""
self.__headers = {
"content-type": "application/json",
"accept": "application/json",
"authorization": self.__token
}
def try_connect(self):
"""Tries to connect the Saildrone API by checking the health endpoint and retrieves access scope"""
# Try to connect to API
if self.__check_health():
# Inflate drones data structure
scope = self.__get_access_scope()
for drone in scope:
self.__drones[drone["drone_id"]] = drone
logging.info("Connected to Saildrone API, {0} drones found.".format(len(self.__drones.keys())))
else:
logging.error("Could not connect to Saildrone API! Service might be down.")
raise SaildroneAPIError("Could not connect to Saildrone API! Service might be down.")
def __check_health(self):
"""Checks the health of the Saildrone API service"""
url = self.__base_url + self.__health_endpoint
response = self.__get(url)
if "success" in response:
return bool(response["success"])
return False
def __get_access_scope(self):
"""Retrieves the access scope of the underlying access token"""
url = self.__base_url + self.__access_endpoint
params = {
"token": self.__token
}
response = self.__get(url, params)
if "success" in response:
if bool(response["success"]):
return response["data"]["access"]
return []
def push_vessel(self, mmsi, timestamp, latitude, longitude, speed_over_ground=0, course_over_ground=0, heading=0):
"""Pushes platforms (unique mmsi/id!) to the Saildrone API AIS endpoint"""
url = self.__base_url + self.__ais_endpoint
params = {
"mmsi": int(mmsi),
"timestamp": int(timestamp),
"latitude": float(latitude),
"longitude": float(longitude),
"sog": float(speed_over_ground),
"cog": float(course_over_ground),
"hdg": float(heading)
}
response = self.__post(url, params)
if "success" in response:
return bool(response["success"])
return False
def get_data(self, drone_id, data_set, start_date, end_date, interval=5, limit=500, order_by="desc", offset=0):
"""Retrieves timeseries data from specified drone limited by various parameters"""
url = self.__base_url + self.__timeseries_endpoint + "/" + str(drone_id)
params = {
"data_set": data_set,
"start_date": start_date,
"end_date": end_date,
"interval": int(interval),
"limit": int(limit),
"order_by": order_by,
"offset": int(offset)
}
return self.__get(url, params)
def __post(self, url, params={}):
"""Generic POST request which works on JSON data"""
try:
req = requests.post(url, json=params, headers=self.__headers)
if not req.ok:
logging.error(f'Got status {req.status_code} for POST:{url}: {req.text}')
raise SaildroneAPIError(f'Got status {req.status_code} for POST:{url}: {req.text}')
return req.json()
except IOError:
logging.error("Error while performing POST request to Saildrone API")
return {}
def __get(self, url, params={}):
"""Generic GET request"""
try:
req = requests.get(url, params, headers=self.__headers)
if not req.ok:
logging.error(f'Got status {req.status_code} for GET:{url}: {req.text}')
raise SaildroneAPIError(f'Got status {req.status_code} for GET:{url}: {req.text}')
return req.json()
except IOError:
logging.error("Error while performing GET request to Saildrone API")
return {}