Skip to content

Commit

Permalink
Started #42: three_hours_forecast + daily_forecast = forecast_at_coords
Browse files Browse the repository at this point in the history
  • Loading branch information
csparpa committed Nov 24, 2019
1 parent 06a96c5 commit 3331dec
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 86 deletions.
75 changes: 26 additions & 49 deletions pyowm/weatherapi25/weather_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,19 +294,22 @@ def forecast_at_place(self, name, interval, limit=None):
else:
return None

def three_hours_forecast_at_coords(self, lat, lon):
def forecast_at_coords(self, lat, lon, interval, limit=None):
"""
Queries the OWM Weather API for three hours weather forecast for the
specified geographic coordinate (eg: latitude: 51.5073509,
longitude: -0.1277583). A *Forecaster* object is returned,
containing a *Forecast* instance covering a global streak of
five days: this instance encapsulates *Weather* objects, with a time
interval of three hours one from each other
Queries the OWM Weather API for weather forecast for the
specified geographic coordinates with the given time granularity.
A *Forecaster* object is returned, containing a *Forecast*: this instance
encapsulates *Weather* objects corresponding to the provided granularity.
:param lat: location's latitude, must be between -90.0 and 90.0
:type lat: int/float
:param lon: location's longitude, must be between -180.0 and 180.0
:type lon: int/float
:param interval: the granularity of the forecast, among `3h` and 'daily'
:type interval: str among `3h` and 'daily'
:param limit: the maximum number of *Weather* items to be retrieved
(default is ``None``, which stands for any number of items)
:type limit: int or ``None``
:returns: a *Forecaster* instance or ``None`` if forecast data is not
available for the specified location
:raises: *ParseResponseException* when OWM Weather API responses' data
Expand All @@ -315,15 +318,29 @@ def three_hours_forecast_at_coords(self, lat, lon):
"""
geo.assert_is_lon(lon)
geo.assert_is_lat(lat)
assert isinstance(interval, str), "Interval must be a string"
if limit is not None:
assert isinstance(limit, int), "'limit' must be an int or None"
if limit < 1:
raise ValueError("'limit' must be None or greater than zero")
params = {'lon': lon, 'lat': lat}
_, json_data = self.http_client.get_json(THREE_HOURS_FORECAST_URI, params=params)
if limit is not None:
params['cnt'] = limit
if interval == '3h':
uri = THREE_HOURS_FORECAST_URI
elif interval == 'daily':
uri = DAILY_FORECAST_URI
else:
raise ValueError("Unsupported time interval for forecast")
_, json_data = self.http_client.get_json(uri, params=params)
fc = forecast.Forecast.from_dict(json_data)
if fc is not None:
fc.interval = "3h"
fc.interval = interval
return forecaster.Forecaster(fc)
else:
return None


def three_hours_forecast_at_id(self, id):
"""
Queries the OWM Weather API for three hours weather forecast for the
Expand Down Expand Up @@ -352,46 +369,6 @@ def three_hours_forecast_at_id(self, id):
else:
return None

def daily_forecast_at_coords(self, lat, lon, limit=None):
"""
Queries the OWM Weather API for daily weather forecast for the specified
geographic coordinate (eg: latitude: 51.5073509, longitude: -0.1277583).
A *Forecaster* object is returned, containing a *Forecast* instance
covering a global streak of fourteen days by default: this instance
encapsulates *Weather* objects, with a time interval of one day one
from each other
:param lat: location's latitude, must be between -90.0 and 90.0
:type lat: int/float
:param lon: location's longitude, must be between -180.0 and 180.0
:type lon: int/float
:param limit: the maximum number of daily *Weather* items to be
retrieved (default is ``None``, which stands for any number of
items)
:type limit: int or ``None``
:returns: a *Forecaster* instance or ``None`` if forecast data is not
available for the specified location
:raises: *ParseResponseException* when OWM Weather API responses' data
cannot be parsed, *APICallException* when OWM Weather API can not be
reached, *ValueError* if negative values are supplied for limit
"""
geo.assert_is_lon(lon)
geo.assert_is_lat(lat)
if limit is not None:
assert isinstance(limit, int), "'limit' must be an int or None"
if limit < 1:
raise ValueError("'limit' must be None or greater than zero")
params = {'lon': lon, 'lat': lat}
if limit is not None:
params['cnt'] = limit
_, json_data = self.http_client.get_json(DAILY_FORECAST_URI, params=params)
fc = forecast.Forecast.from_dict(json_data)
if fc is not None:
fc.interval = "daily"
return forecaster.Forecaster(fc)
else:
return None

def daily_forecast_at_id(self, id, limit=None):
"""
Queries the OWM Weather API for daily weather forecast for the specified
Expand Down
14 changes: 7 additions & 7 deletions tests/integration/weatherapi25/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,14 @@ def test_forecast_at_place_on_3h(self):
for weather in f2:
self.assertTrue(weather is not None)

def test_three_hours_forecast_at_coords(self):
def test_forecast_at_coords_on_3h(self):
"""
Test feature: get 3 hours forecast at a specific geographic coordinate
"""
# London,uk
fc1 = self.__owm.three_hours_forecast_at_coords(51.5073509, -0.1277583)
fc1 = self.__owm.forecast_at_coords(51.5073509, -0.1277583, "3h")
# Kiev
fc2 = self.__owm.three_hours_forecast_at_coords(50.4501, 30.5234)
fc2 = self.__owm.forecast_at_coords(50.4501, 30.5234, "3h")
self.assertTrue(fc1)
f1 = fc1.forecast
self.assertTrue(f1 is not None)
Expand All @@ -234,7 +234,7 @@ def test_three_hours_forecast_at_coords(self):
for weather in f2:
self.assertTrue(weather is not None)
with self.assertRaises(ValueError):
self.__owm.three_hours_forecast_at_coords(199, 199)
self.__owm.forecast_at_coords(199, 199, '3h')

def test_three_hours_forecast_at_id(self):
"""
Expand Down Expand Up @@ -295,11 +295,11 @@ def forecast_at_place_daily(self):
for weather in f2:
self.assertTrue(weather is not None)

def test_daily_forecast_at_coords(self):
def test_forecast_at_coords_daily(self):
"""
Test feature: get daily forecast at a specific geographic coordinate
"""
fc1 = self.__owm.daily_forecast_at_coords(51.5073509, -0.1277583) # London,uk
fc1 = self.__owm.forecast_at_coords(51.5073509, -0.1277583, 'daily') # London,uk
self.assertTrue(fc1)
f1 = fc1.forecast
self.assertTrue(f1 is not None)
Expand All @@ -310,7 +310,7 @@ def test_daily_forecast_at_coords(self):
for weather in f1:
self.assertTrue(weather is not None)
with self.assertRaises(ValueError):
self.__owm.daily_forecast_at_coords(199, 199)
self.__owm.forecast_at_coords(199, 199, 'daily')

def test_daily_forecast_at_id(self):
"""
Expand Down
70 changes: 40 additions & 30 deletions tests/unit/weatherapi25/test_weather_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,29 @@ def test_forecast_at_place_on_3h_when_forecast_not_found(self):
HttpClient.get_json = original_func
self.assertIsNone(result)

def test_three_hours_forecast_at_coords(self):
def test_forecast_at_coords_failing(self):
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, -100.0, 0.0, '3h', None)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 100.0, 0.0, '3h', None)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, -200.0, '3h', None)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 200.0, '3h', None)
self.assertRaises(AssertionError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, None, None)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, 'unsupported', None)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, '3h', -4)

def test_forecast_at_coords_on_3h(self):
original_func = HttpClient.get_json
HttpClient.get_json = \
self.mock_api_call_returning_3h_forecast_at_coords
result = \
self.__test_instance\
.three_hours_forecast_at_coords(51.50853, -0.12574)
.forecast_at_coords(51.50853, -0.12574, "3h")
HttpClient.get_json = original_func
self.assertTrue(isinstance(result, Forecaster))
forecast = result.forecast
Expand All @@ -290,24 +306,14 @@ def test_three_hours_forecast_at_coords(self):
for weather in forecast:
self.assertTrue(isinstance(weather, Weather))

def test_three_hours_forecast_at_coords_when_forecast_not_found(self):
def test_forecast_at_coords_on_3h_when_forecast_not_found(self):
original_func = HttpClient.get_json
HttpClient.get_json = \
self.mock_api_call_returning_empty_3h_forecast
result = self.__test_instance.three_hours_forecast_at_coords(51.50853, -0.12574)
result = self.__test_instance.forecast_at_coords(51.50853, -0.12574, '3h')
HttpClient.get_json = original_func
self.assertIsNone(result)

def test_three_hours_forecast_at_coords_fails_with_wrong_params(self):
self.assertRaises(ValueError, WeatherManager.three_hours_forecast_at_coords,
self.__test_instance, -100.0, 0.0)
self.assertRaises(ValueError, WeatherManager.three_hours_forecast_at_coords,
self.__test_instance, 100.0, 0.0)
self.assertRaises(ValueError, WeatherManager.three_hours_forecast_at_coords,
self.__test_instance, 0.0, -200.0)
self.assertRaises(ValueError, WeatherManager.three_hours_forecast_at_coords,
self.__test_instance, 0.0, 200.0)

def test_three_hours_forecast_at_id(self):
original_func = HttpClient.get_json
HttpClient.get_json = \
Expand Down Expand Up @@ -360,12 +366,12 @@ def test_forecast_at_place_daily_when_forecast_not_found(self):
HttpClient.get_json = original_func
self.assertIsNone(result)

def test_daily_forecast_at_coords(self):
def test_forecast_at_coords_daily(self):
original_func = HttpClient.get_json
HttpClient.get_json = \
self.mock_api_call_returning_daily_forecast_at_coords
result = \
self.__test_instance.daily_forecast_at_coords(51.50853, -0.12574, 2)
self.__test_instance.forecast_at_coords(51.50853, -0.12574, 'daily', 2)
HttpClient.get_json = original_func
self.assertTrue(isinstance(result, Forecaster))
forecast = result.forecast
Expand All @@ -377,23 +383,27 @@ def test_daily_forecast_at_coords(self):
for weather in forecast:
self.assertTrue(isinstance(weather, Weather))

def test_daily_forecast_at_coords_fails_with_wrong_parameters(self):
self.assertRaises(ValueError, WeatherManager.daily_forecast_at_coords,
self.__test_instance, 51.50853, -0.12574, -3)
self.assertRaises(ValueError, WeatherManager.daily_forecast_at_coords,
self.__test_instance, -100.0, 0.0)
self.assertRaises(ValueError, WeatherManager.daily_forecast_at_coords,
self.__test_instance, 100.0, 0.0)
self.assertRaises(ValueError, WeatherManager.daily_forecast_at_coords,
self.__test_instance, 0.0, -200.0)
self.assertRaises(ValueError, WeatherManager.daily_forecast_at_coords,
self.__test_instance, 0.0, 200.0)

def test_daily_forecast_at_coords_when_forecast_not_found(self):
def test_forecast_at_coords_daily_fails_with_wrong_parameters(self):
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, -100.0, 0.0, 'daily')
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 100.0, 0.0, 'daily')
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, -200.0, 'daily')
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 200.0, 'daily')
self.assertRaises(AssertionError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, None, 2)
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, 'unsupported')
self.assertRaises(ValueError, WeatherManager.forecast_at_coords,
self.__test_instance, 0.0, 60.0, 'daily', -5)

def test_forecast_at_coords_dailty_when_forecast_not_found(self):
original_func = HttpClient.get_json
HttpClient.get_json = \
self.mock_api_call_returning_empty_daily_forecast
result = self.__test_instance.daily_forecast_at_coords(51.50853, -0.12574)
result = self.__test_instance.forecast_at_coords(51.50853, -0.12574, 'daily')
HttpClient.get_json = original_func
self.assertIsNone(result)

Expand Down

0 comments on commit 3331dec

Please sign in to comment.