# coding: utf-8 import json import logging import re import urllib.request, urllib.error, urllib.parse from django.conf import settings wu_base_url = 'http://api.wunderground.com/api/{0:s}/'.format(settings.WEATHER_WEATHER_UNDERGROUND_API_KEY) log = logging.getLogger('weather.lib') def get_conditions_for_query(queryitems): """Make a wunderground conditions call, return as string.""" # recombine the query into a string query = ' '.join(queryitems) query = query.replace(' ', '_') try: url = wu_base_url + ('{0:s}/q/{1:s}.json'.format('conditions', query)) log.debug("calling %s", url) json_resp = urllib.request.urlopen(url) condition_data = json.load(json_resp) except IOError as e: log.error("error while making conditions query") log.exception(e) raise # condition data is loaded. the rest of this is obviously specific to # http://www.wunderground.com/weather/api/d/docs?d=data/conditions log.debug(json.dumps(condition_data, sort_keys=True, indent=4)) try: # just see if we have current_observation data current = condition_data['current_observation'] except KeyError as e: # ok, try to see if the ambiguous results stuff will help log.debug(e) log.debug("potentially ambiguous results, checking") try: results = condition_data['response']['results'] reply = "Multiple results, try one of the following zmw codes:" for res in results[:-1]: q = res['l'].strip('/q/') reply += " {0:s} ({1:s}, {2:s}),".format(q, res['name'], res['country_name']) q = results[-1]['l'].strip('/q/') reply += " or {0:s} ({1:s}, {2:s}).".format(q, results[-1]['name'], results[-1]['country_name']) return reply except KeyError as e: # now we really know something is wrong log.error("error or bad query in conditions lookup") log.exception(e) return "No results." else: try: location = current['display_location']['full'] reply = "Conditions for {0:s}: ".format(location) weather_str = current['weather'] if weather_str != '': reply += "{0:s}, ".format(weather_str) temp_f = current['temp_f'] temp_c = current['temp_c'] temp_str = current['temperature_string'] if temp_f != '' and temp_c != '': reply += "{0:.1f}°F ({1:.1f}°C)".format(temp_f, temp_c) elif temp_str != '': reply += "{0:s}".format(temp_str) # append feels like if we have it feelslike_f = current['feelslike_f'] feelslike_c = current['feelslike_c'] feelslike_str = current['feelslike_string'] if feelslike_f != '' and feelslike_c != '': reply += ", feels like {0:s}°F ({1:s}°C)".format(feelslike_f, feelslike_c) elif feelslike_str != '': reply += ", feels like {0:s}".format(feelslike_str) # whether this is current or current + feelslike, terminate sentence reply += ". " humidity_str = current['relative_humidity'] if humidity_str != '': reply += "Humidity: {0:s}. ".format(humidity_str) wind_str = current['wind_string'] if wind_str != '': reply += "Wind: {0:s}. ".format(wind_str) pressure_in = current['pressure_in'] pressure_trend = current['pressure_trend'] if pressure_in != '': reply += "Pressure: {0:s}\"".format(pressure_in) if pressure_trend != '': reply += " and {0:s}".format("dropping" if pressure_trend == '-' else "rising") reply += ". " heat_index_str = current['heat_index_string'] if heat_index_str != '' and heat_index_str != 'NA': reply += "Heat index: {0:s}. ".format(heat_index_str) windchill_str = current['windchill_string'] if windchill_str != '' and windchill_str != 'NA': reply += "Wind chill: {0:s}. ".format(windchill_str) visibility_mi = current['visibility_mi'] if visibility_mi != '': reply += "Visibility: {0:s} miles. ".format(visibility_mi) precip_in = current['precip_today_in'] if precip_in != '': reply += "Precipitation today: {0:s}\". ".format(precip_in) observation_time = current['observation_time'] if observation_time != '': reply += "{0:s}. ".format(observation_time) return _prettify_weather_strings(reply.rstrip()) except KeyError as e: log.error("error or unexpected results in conditions reply") log.exception(e) return "Error parsing results." def get_forecast_for_query(queryitems): """Make a wunderground forecast call, return as string.""" # recombine the query into a string query = ' '.join(queryitems) query = query.replace(' ', '_') try: url = wu_base_url + ('{0:s}/q/{1:s}.json'.format('forecast', query)) json_resp = urllib.request.urlopen(url) forecast_data = json.load(json_resp) except IOError as e: log.error("error while making forecast query") log.exception(e) raise # forecast data is loaded. the rest of this is obviously specific to # http://www.wunderground.com/weather/api/d/docs?d=data/forecast log.debug(json.dumps(forecast_data, sort_keys=True, indent=4)) try: # just see if we have forecast data forecasts = forecast_data['forecast']['txt_forecast'] except KeyError as e: # ok, try to see if the ambiguous results stuff will help log.debug(e) log.debug("potentially ambiguous results, checking") try: results = forecast_data['response']['results'] reply = "Multiple results, try one of the following zmw codes:" for res in results[:-1]: q = res['l'].strip('/q/') reply += " {0:s} ({1:s}, {2:s}),".format(q, res['name'], res['country_name']) q = results[-1]['l'].strip('/q/') reply += " or {0:s} ({1:s}, {2:s}).".format(q, results[-1]['name'], results[-1]['country_name']) return reply except KeyError as e: # now we really know something is wrong log.error("error or bad query in forecast lookup") log.exception(e) return "No results." else: try: reply = "Forecast: " for forecast in forecasts['forecastday'][0:5]: reply += "{0:s}: {1:s} ".format(forecast['title'], forecast['fcttext']) return _prettify_weather_strings(reply.rstrip()) except KeyError as e: log.error("error or unexpected results in forecast reply") log.exception(e) return "Error parsing results." def _prettify_weather_strings(weather_str): """ Clean up output strings. For example, turn 32F into 32°F in input string. Input: weather_str --- the string to clean up """ return re.sub(r'(\d+)\s*([FC])', r'\1°\2', weather_str)