"""Module to connect to the dd-api-oper This module contains the following classes: * ddApiOper: Main class to connect to the dd api oper interface * ddApiResult: Super class for results - ddApiLocation: Result class for locations - ddApiQuantitie: Result class for quantities - ddApiValues: Result class for values * ddApiOperHttpError: Exception class for http errors See: https://digitaledeltaorg.github.io/dd-oper.v201.html """ import logging import requests from datetime import datetime, timedelta class ddApiOper: """ Class that connects to the digitale delta api with a client certificate. Methods for retrieving: - The list of locations - The list of quantitiess - The quantities of a location - Values for a given process, location and quantity """ RWS_DD_API = "https://ddapi.rws.nl/dd-oper/2.0" def __init__(self, certfile=None, certkey=None, url=RWS_DD_API, checkssl=True): """ddApiOper(certfile, certKey, url="https://ddapi.rws.nl/dd-oper/2.0", checkssl=True) certfile: x509 pki overheidscertificaat certKey: Unencrypted private key url(optional): dd Api url (default https://ddapi.rws.nl/dd-oper/2.0) checkssl(optional): Check ssl keychain (default true) Details for the certificate files see: https://requests.readthedocs.io/en/master/user/advanced/#client-side-certificates """ self.certfile = certfile self.certkey = certkey self.url = url self.checkssl = checkssl logging.debug("ddApiOper.__init__(%s)" % url) def get(self, url): """get(url) Get the json data from the dd-api with pyton request. Note: Internal use! """ logging.debug("ddApiOper.get(%s)" % url) try: req = requests.get(url, cert=(self.certfile, self.certkey)) except Exception as e: logging.exception("ddApiOper.get error: %s" % e) raise(e) if (req.status_code != 200): raise ddApiOperHttpError(req.status_code) return(req.json()) def locations(self): """locations() Request the list of locations and return a ddApiLocation object. """ return(ddApiLocation(self.get(self.url + "/locations")["results"])) def quantities(self): """quantities() Request the list of quantities and return a ddApiQuntitie object. """ return(ddApiQuantitie(self.get(self.url + "/quantities")["results"])) def locationQuantities(self, location): """locationQuantities(location) location: A dd-api location (e.g. hoekvanholland) Request the list of quantities from a location and return a ddApiQuntitie object. """ return(ddApiQuantitie(self.get(self.url + "/locations/%s/quantities" % location)["results"])) def values(self, location, quantitie, process="measurement", starttime=None, endtime=None, intervalLength="10min", aspectset="minimum"): """values(location, quantitie, process="measurement", starttime=None, endtime=None, intervalLength="10min", aspectset="minimum") location: A dd-api location (e.g. hoekvanholland) quantitie: A dd-api quantitie (e.g. waterlevel) process(optional): Default measurement (forecast, astronomical, advise) starttime(optional): Start of the time serie (Default 24 hours ago) endtime(optional): End of the time serie (Default now) intervalLength(optional): Interval between measurements (default 10 min) aspectset(optional): Default minimum (normal, maximum) """ if not endtime: endtime = datetime.utcnow() if not starttime: starttime = endtime - timedelta(days=1) if type(starttime) == datetime: starttime = starttime.isoformat() + "Z" if type(endtime) == datetime: endtime = endtime.isoformat() + "Z" logging.debug("ddApiOper.values(%s, %s, %s, %s, %s, %s, %s)" % ( location, quantitie, process, starttime, endtime, intervalLength, aspectset)) return(ddApiValues(self.get(( "%s" "/locations/%s/quantities/%s" "/timeseries?&startTime=%s&endTime=%s" "&process=%s&intervalLength=%s&aspectSet=%s" ) % ( self.url, location, quantitie, starttime, endtime, process, intervalLength, aspectset )))) class ddApiResult(object): """ Class ddApiResult: Base class for ddApiResults. """ def __init__(self, data): """ddApiResult(data)""" self.data = data class ddApiLocation(ddApiResult): """ Class ddApiLocation: ddApi Result class for locations. """ def __init__(self, data): super().__init__(data) self.createIndex() def createIndex(self): """createIndex() Note: Internal function """ self.__index = {} i = 0 for loc in self.data: self.__index[loc["properties"]["locationName"]] = i i+=1 def locationNames(self): """locationNames() Returns a list of ddapi location names """ return(self.__index.keys()) def locationDetail(self, locationName): """locationDetail(locationName): Returns the detais of a location from the list """ return(self.data[self.__index[locationName]]) def displayNameGlobal(self, locationName): """displayNameGlobal(locationName) Returns the global display name of a location """ return(self.locationDetail(locationName)["properties"]["displayNameGlobal"]) def coordinate(self, locationName): """coordinate(locationName) Returns the coordinates of a location """ return(self.locationDetail(locationName)["geometry"]["coordinates"]) class ddApiQuantitie(ddApiResult): """ Class ddApiQuantitie: ddApi Result class for Quantities. """ def quantities(self): """quantities() Returns a list of quantities """ return(self.data) class ddApiValues(ddApiResult): """ Class ddApiValues: ddApi Result class for values. """ def provider(self): """provider() Returns the provider metadata. """ return(self.data["provider"]) def result(self, index=0): """result(index=0) Returns the results part of the response. """ return(self.data["results"][index]) def aspectSet(self): """aspectSet() Returns the metadata of the aspectSet """ return(self.result()["observationType"]["aspectSet"]["aspects"]) def location(self): """location() Returns the location metadata """ return(self.result()["location"]) def source(self): """source() Returns the source metadata """ return(self.result()["source"]) def values(self, index=0): """values(index=0) Returns the values at ittorator of tupples. (time, value) (time, value) Note: Is this the correct return data format?? This may change! """ for e in self.result()["events"]: yield (e["timeStamp"], e["aspects"][index]["points"][0]["value"]) def sip(self, index=0): """sip(index) Returns the result set as string in the sip format! """ r = "!" sep = " " for e in self.result()["events"]: v = e["aspects"][index]["points"][0]["value"] q = e["aspects"][index]["points"][0]["quality"] r += "%s%i/%i" % (sep, v, q) sep = ";" return(r) class ddApiOperHttpError(Exception): """ Class ddApiOperHttpError Exception class to use on http errors. """ def __init__(self, code): self.code = code if __name__ == "__main__": pass