/pywunderground/weather_underground.py
https://gitlab.com/cburki/python_wunderground · Python · 361 lines · 109 code · 74 blank · 178 comment · 8 complexity · c5caa694d4606480b769d6c92714bfb3 MD5 · raw file
- #
- # Filename : weather_underground.py
- # Description :
- # Author : Christophe Burki
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License version 3 as
- # published by the Free Software Foundation.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; see the file LICENSE. If not, write to the
- # Free Software Foundation, Inc., 51 Franklin Street, Fifth
- # ;; Floor, Boston, MA 02110-1301, USA.
- # ------------------------------------------------------------------------------
- from urllib3 import PoolManager
- from urllib3.exceptions import ConnectTimeoutError, ReadTimeoutError
- from json import loads, dumps
- from pywunderground.observation_parser import ObservationParser
- from pywunderground.daily_forecast_parser import DailyForecastParser
- from pywunderground.hourly_forecast_parser import HourlyForecastParser
- from pywunderground.astronomy_parser import AstronomyParser
- from pywunderground.exceptions import APICallError, APIResponseError
- # ------------------------------------------------------------------------------
- class WeatherUnderground:
- """
- Class providing methods for querying the API.
- """
- URL = 'http://api.wunderground.com/api/{apikey}/{features}/q/{location}.json'
- QUERY_TIMEOUT = 30
- FEATURES = ['conditions', 'forecast', 'forecast10day', 'hourly', 'astronomy', 'geolookup']
- API_FEATURES = {'conditions': ('observation', 'current_observation'),
- 'forecast': ('daily_forecast', 'forecast'),
- 'forecast10day': ('daily_forecast', 'forecast'),
- 'hourly': ('hourly_forecast', 'hourly_forecast'),
- 'astronomy': ('astronomy', 'moon_phase')}
- # --------------------------------------------------------------------------
- def __init__(self, apiKey, cache):
- """
- Constructor
- :param apiKey: The weather underground API key
- :type apiKey: string
- :param cache:
- :type cache:
- """
- self._apiKey = apiKey
- self._cache = cache
- self._api = PoolManager(timeout=WeatherUnderground.QUERY_TIMEOUT)
- # --------------------------------------------------------------------------
- def __del__(self):
- """
- Destructor
- """
- self._api.clear()
- # --------------------------------------------------------------------------
- def weatherAtPlace(self, place):
- """
- Query the WU API for the weather currently observed at the given place.
- :param place: The location where to observe weather (country,city)
- :type place: string
- """
- data = self._query('conditions/forecast/astronomy', place)
- observation = ObservationParser.parse(data)
- return observation
- # --------------------------------------------------------------------------
- def weatherAtCoords(self, latitude, longitude):
- """
- Query the WU API for the weather currently observed at the given
- geographic location.
- :param latitude: The location latitude
- :type latitude: float
- :param longitude: The location longitude
- :type longitude: float
- """
- data = self._query('conditions/forecast/astronomy', '{},{}'.format(latitude, longitude))
- observation = ObservationParser.parse(data)
- return observation
- # --------------------------------------------------------------------------
- def weatherAtAirport(self, code):
- """
- Query the WU API for the weather currently observed at the given
- airport code.
- :param code: The airport code where to observe weather
- :type code: string
- """
- data = self._query('conditions/forecast/astronomy', code)
- observation = ObservationParser.parse(data)
- return observation
- # --------------------------------------------------------------------------
- def weatherAtPWSId(self, id):
- """
- Query the WU API for the weather currently observed at the given PWS
- (Personal Weather Station) ID.
- :param id: The PWS identifier where to observe the weather
- :type id: string
- """
- data = self._query('conditions/forecast/astronomy', 'pws:{}'.format(id))
- observation = ObservationParser.parse(data)
- return observation
- # --------------------------------------------------------------------------
- def dailyForecastAtPlace(self, place):
- """
- Query the WU API for the daily weather forecast at the given place.
- :param place: The location where to observe weather (country,city)
- :type place: string
- """
- data = self._query('conditions/forecast10day', place)
- forecast = DailyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def dailyForecastAtCoords(self, latitude, longitude):
- """
- Query the WU API for the daily weather forecast at the given
- geographic location.
- :param latitude: The location latitude
- :type latitude: float
- :param longitude: The location longitude
- :type longitude: float
- """
- data = self._query('conditions/forecast10day', '{},{}'.format(latitude, longitude))
- forecast = DailyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def dailyForecastAtAirport(self, code):
- """
- Query the WU API for the daily weather forecast at the given
- airport code.
- :param code: The airport code where to observe weather
- :type code: string
- """
- data = self._query('conditions/forecast10day', code)
- forecast = DailyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def dailyForecastAtPWSId(self, id):
- """
- Query the WU API for the daily weather forecast at the given PWS
- (Personal Weather Station) ID.
- :param id: The PWS (Personal Weather Station) identifier where to observe the weather
- :type id: string
- """
- data = self._query('conditions/forecast10day', 'pws:{}'.format(id))
- forecast = DailyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def hourlyForecastAtPlace(self, place):
- """
- Query the WU API for the hourly weather forecast at the given place.
- :param place: The location where to observe weather (country/city)
- :type place: string
- """
- data = self._query('conditions/hourly', place)
- forecast = HourlyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def hourlyForecastAtCoords(self, latitude, longitude):
- """
- Query the WU API for the hourly weather forecast at the given
- geographic location.
- :param latitude: The location latitude
- :type latitude: float
- :param longitude: The location longitude
- :type longitude: float
- """
- data = self._query('conditions/hourly', '{},{}'.format(latitude, longitude))
- forecast = HourlyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def hourlyForecastAtAirport(self, code):
- """
- Query the WU API for the hourly weather forecast at the given
- airport code.
- :param code: The airport code where to observe weather
- :type code: string
- """
- data = self._query('conditions/hourly', code)
- forecast = HourlyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def hourlyForecastAtPWSId(self, id):
- """
- Query the WU API for the hourly weather forecast at the given PWS
- (Personal Weather Station) ID.
- :param id: The PWS (Personal Weather Station) identifier where to observe the weather
- :type id: string
- """
- data = self._query('conditions/hourly', 'pws:{}'.format(id))
- forecast = HourlyForecastParser.parse(data)
- return forecast
- # --------------------------------------------------------------------------
- def astronomyAtPlace(self, place):
- """
- Query the WU API for astronomy at the given place.
- :param place: The location where to get the astronomy (country/city)
- :type place: string
- """
- data = self._query('astronomy', place)
- astronomy = AstronomyParser.parse(data)
- return astronomy
- # --------------------------------------------------------------------------
- def astronomyAtCoords(self, latitude, longitude):
- """
- Query the WU API for the astronomy at the given geographic location.
- :param latitude: The location latitude
- :type latitude: float
- :param longitude: The location longitude
- :type longitude: float
- """
- data = self._query('astronomy', '{},{}'.format(latitude, longitude))
- astronomy = AstronomyParser.parse(data)
- return astronomy
- # --------------------------------------------------------------------------
- def astronomyAtAirport(self, code):
- """
- Query the WU API for the astronomy at the given airport code.
- :param code: The airport code where to get the astronomy
- :type code: string
- """
- data = self._query('astronomy', code)
- astronomy = AstronomyParser.parse(data)
- return astronomy
- # --------------------------------------------------------------------------
- def astronomyAtPWSId(self, id):
- """
- Query the WU API for the astronomy at the given PWS (Personal Weather
- Station) ID.
- :param id: The PWS (Personal Weather Station) identifier where to get the astronomy
- :type id: string
- """
- data = self._query('astronomy', 'pws:{}'.format(id))
- astronomy = AstronomyParser.parse(data)
- return astronomy
- # --------------------------------------------------------------------------
- def _query(self, features, location):
- """
- Query the API for the given features and location.
- :param features: Features formatted string to query
- :type features: string
- :param location: The location for which to make the query
- :type location: string
- """
- url = WeatherUnderground.URL.format(apikey=self._apiKey, features=features, location=location)
- try:
- request = self._api.request('GET', url)
- except ConnectTimeoutError as e:
- raise APICallError('Timeout error while querying API', e)
- except ReadTimeoutError as e:
- raise APICallError('Timeout error while querying API', e)
- except Exception as e:
- raise APICallError('Error while querying API', e)
- if request.status != 200:
- raise APIResponseError('Error during HTTP request', request.status)
- apiData = loads(request.data.decode('utf-8'))
- # print("apiData={}".format(apiData))
- if 'error' in apiData['response']:
- error = apiData['response']['error']
- raise APIResponseError('Error from API', error['type'])
- if len(apiData['response']['features']) != len(features.split('/')):
- raise APIResponseError('Not all features retrieved', dumps(apiData['response']['features']))
- # Keep only the data for the requested features.
- data = {}
- for feature in features.split('/'):
- data[WeatherUnderground.API_FEATURES[feature][0]] = apiData[WeatherUnderground.API_FEATURES[feature][1]]
- return data
- # ------------------------------------------------------------------------------