Skip to content

Commit

Permalink
Fixes #258
Browse files Browse the repository at this point in the history
  • Loading branch information
csparpa committed Oct 16, 2018
1 parent 279d75d commit b8d85dc
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 8 deletions.
1 change: 1 addition & 0 deletions pyowm/weatherapi25/configuration25.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
FIND_OBSERVATIONS_URL = ROOT_API_URL + '/find'
FIND_STATION_URL = ROOT_API_URL + '/station/find'
BBOX_STATION_URL = ROOT_API_URL + '/box/station'
BBOX_CITY_URL = ROOT_API_URL + '/box/city'
THREE_HOURS_FORECAST_URL = ROOT_API_URL + '/forecast'
DAILY_FORECAST_URL = ROOT_API_URL + '/forecast/daily'
CITY_WEATHER_HISTORY_URL = ROOT_HISTORY_URL + '/history/city'
Expand Down
52 changes: 51 additions & 1 deletion pyowm/weatherapi25/owm25.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
OBSERVATION_URL, GROUP_OBSERVATIONS_URL,
FIND_OBSERVATIONS_URL, THREE_HOURS_FORECAST_URL,
DAILY_FORECAST_URL, CITY_WEATHER_HISTORY_URL, STATION_WEATHER_HISTORY_URL,
FIND_STATION_URL, STATION_URL, BBOX_STATION_URL)
FIND_STATION_URL, STATION_URL, BBOX_STATION_URL, BBOX_CITY_URL)
from pyowm.weatherapi25.configuration25 import city_id_registry as reg
from pyowm.abstractions import owm
from pyowm.abstractions.decorators import deprecated
Expand Down Expand Up @@ -439,6 +439,56 @@ def weather_at_stations_in_bbox(self, lat_top_left, lon_top_left,
_, json_data = self._wapi.cacheable_get_json(uri, params=params)
return self._parsers['observation_list'].parse_JSON(json_data)

def weather_at_places_in_bbox(self, lon_left, lat_bottom, lon_right, lat_top,
zoom=10, cluster=False):
"""
Queries the OWM Weather API for the weather currently observed by
meteostations inside the bounding box of latitude/longitude coords.
:param lat_top: latitude for top margin of bounding box, must be
between -90.0 and 90.0
:type lat_top: int/float
:param lon_left: longitude for left margin of bounding box
must be between -180.0 and 180.0
:type lon_left: int/float
:param lat_bottom: latitude for the bottom margin of bounding box, must
be between -90.0 and 90.0
:type lat_bottom: int/float
:param lon_right: longitude for the right margin of bounding box,
must be between -180.0 and 180.0
:type lon_right: int/float
:param zoom: zoom level (defaults to: 10)
:type zoom: int
:param cluster: use server clustering of points
:type cluster: bool
:returns: a list of *Observation* objects or ``None`` if no weather
data is available
:raises: *ParseResponseException* when OWM Weather API responses' data
cannot be parsed, *APICallException* when OWM Weather API can not be
reached, *ValueError* when coordinates values are out of bounds or
negative values are provided for limit
"""
geo.assert_is_lon(lon_left)
geo.assert_is_lon(lon_right)
geo.assert_is_lat(lat_bottom)
geo.assert_is_lat(lat_top)
assert type(zoom) is int, "'zoom' must be an int"
if zoom <= 0:
raise ValueError("'zoom' must greater than zero")
assert type(cluster) is bool, "'cluster' must be a bool"
params = {'bbox': ','.join([str(lon_left),
str(lat_bottom),
str(lon_right),
str(lat_top),
str(zoom)]),
'cluster': 'yes' if cluster else 'no'}
uri = http_client.HttpClient.to_url(BBOX_CITY_URL,
self._API_key,
self._subscription_type,
self._use_ssl)
_, json_data = self._wapi.cacheable_get_json(uri, params=params)
return self._parsers['observation_list'].parse_JSON(json_data)

def weather_around_coords(self, lat, lon, limit=None):
"""
Queries the OWM Weather API for the currently observed weather in all the
Expand Down
16 changes: 10 additions & 6 deletions pyowm/weatherapi25/parsers/observationlistparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,20 @@ def parse_JSON(self, JSON_string):
# of HTTP error status codes by the OWM API 2.5. This mechanism is
# supposed to be deprecated as soon as the API fully adopts HTTP for
# conveying errors to the clients
if d['cod'] == "404":
print("OWM API: data not found - response payload: " + \
json.dumps(d))
return None
if d['cod'] != "200":
raise APIResponseError("OWM API: error - response payload: " + json.dumps(d), d['cod'])
if d['cod'] == "200" or d['cod'] == 200:
pass
else:
if d['cod'] == "404" or d['cod'] == 404:
print("OWM API: data not found - response payload: " + json.dumps(d))
return None
else:
raise APIResponseError("OWM API: error - response payload: " + json.dumps(d), str(d['cod']))

# Handle the case when no results are found
if 'count' in d and d['count'] == "0":
return []
if 'cnt' in d and d['cnt'] == 0:
return []
if 'list' in d:
return [observation_parser.parse_JSON(json.dumps(item)) \
for item in d['list']]
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/weatherapi25/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,17 @@ def test_weather_at_stations_in_bbox(self):
weat = item.get_weather()
self.assertTrue(weat is not None)

def test_weather_at_places_in_bbox(self):
o = self.__owm.weather_at_places_in_bbox(0.734720, 38.422663, 1.964651, 39.397204, 10, False) # Ibiza
self.assertTrue(isinstance(o, list))
for item in o:
self.assertTrue(item is not None)
self.assertTrue(item.get_reception_time() is not None)
loc = item.get_location()
self.assertTrue(loc is not None)
weat = item.get_weather()
self.assertTrue(weat is not None)


if __name__ == "__main__":
unittest.main()
10 changes: 10 additions & 0 deletions tests/unit/weatherapi25/json_test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
',"dt":1419210000,"id":7343,"main":{"pressure":1007,"temp":0},"name":'\
'"UWSS","rang":50,"type":1,"wind":{"deg":180,"speed":3}}]}'

WEATHER_AT_PLACES_IN_BBOX_JSON = '{"cod":"200","calctime":0.3107,"cnt":2,' \
'"list":[{"id":2208791,"name":"Yafran","coord":{"lon":12.52859,"lat":32.06329},"main":{"temp":9.68,"temp_min":9.681,' \
'"temp_max":9.681,"pressure":961.02,"sea_level":1036.82,"grnd_level":961.02,"humidity":85},"dt":1485784982,' \
'"wind":{"speed":3.96,"deg":356.5},"rain":{"3h":0.255},"clouds":{"all":88},"weather":[{"id":500,"main":"Rain",' \
'"description":"lightrain","icon":"10d"}]},{"id":2208425,"name":"Zuwarah","coord":{"lon":12.08199,"lat":32.931198},' \
'"main":{"temp":15.36,"temp_min":15.356,"temp_max":15.356,"pressure":1036.81,"sea_level":1037.79,' \
'"grnd_level":1036.81,"humidity":89},"dt":1485784982,"wind":{"speed":5.46,"deg":30.0002},"clouds":{"all":56},' \
'"weather":[{"id":803,"main":"Clouds","description":"brokenclouds","icon":"04d"}]}]}'


SEARCH_RESULTS_JSON = '{"cod": "200", "count": 2, "list": [{"clouds": {"all": ' \
'20}, "coord": {"lat": 51.50853, "lon": -0.12573999999999999}, "dt": 1378237178,' \
' "id": 2643743, "main": {"humidity": 56, "pressure": 1025, "temp": ' \
Expand Down
28 changes: 27 additions & 1 deletion tests/unit/weatherapi25/test_owm25.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
STATION_WEATHER_HISTORY_JSON, THREE_HOURS_FORECAST_NOT_FOUND_JSON,
DAILY_FORECAST_NOT_FOUND_JSON, STATION_HISTORY_NO_ITEMS_JSON,
STATION_OBSERVATION_JSON, STATION_AT_COORDS_JSON,
WEATHER_AT_STATION_IN_BBOX_JSON)
WEATHER_AT_STATION_IN_BBOX_JSON, WEATHER_AT_PLACES_IN_BBOX_JSON)
from tests.unit.uvindexapi30.test_uvindexparser import UVINDEX_JSON
from tests.unit.uvindexapi30.test_uvindexlistparser import UVINDEX_LIST_JSON
from tests.unit.pollutionapi30.test_parsers import COINDEX_JSON, OZONE_JSON, NO2INDEX_JSON, SO2INDEX_JSON
Expand Down Expand Up @@ -135,6 +135,9 @@ def mock_call_api_returning_station_history_with_no_items(self, uri, params=None
def mock_api_call_returning_weather_at_stations_in_bbox(self, uri, params=None, headers=None):
return 200, WEATHER_AT_STATION_IN_BBOX_JSON

def mock_api_call_returning_weather_at_places_in_bbox(self, uri, params=None, headers=None):
return 200, WEATHER_AT_PLACES_IN_BBOX_JSON

def mock_api_call_returning_station_at_coords(self, uri, params=None, headers=None):
return 200, STATION_AT_COORDS_JSON

Expand Down Expand Up @@ -742,6 +745,29 @@ def test_weather_at_station_in_bbox(self):
self.assertTrue(isinstance(result.get_location(), Location))
self.assertTrue(result.get_reception_time() is not None)

def test_weather_at_places_in_bbox_fails_with_wrong_params(self):
self.assertRaises(AssertionError, OWM25.weather_at_places_in_bbox,
self.__test_instance, 12, 32, 15, 37, 'zoom')
self.assertRaises(ValueError, OWM25.weather_at_places_in_bbox,
self.__test_instance, 12, 32, 15, 37, -30)
self.assertRaises(AssertionError, OWM25.weather_at_places_in_bbox,
self.__test_instance, 12, 32, 15, 37, 10, 'cluster')

def test_weather_at_places_in_bbox(self):
original_func = HttpClient.cacheable_get_json
HttpClient.cacheable_get_json = \
self.mock_api_call_returning_weather_at_places_in_bbox
results = self.__test_instance\
.weather_at_places_in_bbox(12,32,15,37,10)
HttpClient.cacheable_get_json = original_func
self.assertTrue(isinstance(results, list))
for result in results:
self.assertTrue(isinstance(result, Observation))
self.assertTrue(isinstance(result.get_weather(), Weather))
self.assertTrue(isinstance(result.get_location(), Location))
self.assertTrue(result.get_reception_time() is not None)


def test_station_tick_history(self):
original_func = HttpClient.cacheable_get_json
HttpClient.cacheable_get_json = \
Expand Down

0 comments on commit b8d85dc

Please sign in to comment.