diff --git a/weather/ircplugin.py b/weather/ircplugin.py index 226b674..4b931d5 100644 --- a/weather/ircplugin.py +++ b/weather/ircplugin.py @@ -2,7 +2,7 @@ import logging from ircbot.lib import Plugin -from weather.lib import get_conditions_for_query, get_forecast_for_query +from weather.lib import weather_summary log = logging.getLogger('weather.ircplugin') @@ -28,27 +28,12 @@ class Weather(Plugin): super(Weather, self).stop() def handle_weather(self, connection, event, match): - """Handle IRC input for a weather queries.""" - - # see if there was a specific command given in the query, first query = match.group(1) queryitems = query.split(" ") if len(queryitems) <= 0: return - # search for commands - if queryitems[0] == 'conditions': - # current weather query - results = get_conditions_for_query(queryitems[1:]) - return self.bot.reply(event, results) - elif queryitems[0] == 'forecast': - # forecast query - results = get_forecast_for_query(queryitems[1:]) - return self.bot.reply(event, results) - else: - # assume they wanted current weather - results = get_conditions_for_query(queryitems) - return self.bot.reply(event, results) + return self.bot.reply(event, weather_summary(queryitems[0])) plugin = Weather diff --git a/weather/lib.py b/weather/lib.py index 86d73cc..afa0d12 100644 --- a/weather/lib.py +++ b/weather/lib.py @@ -1,196 +1,50 @@ # coding: utf-8 - +"""Get results of weather queries.""" import logging -import re import requests -from django.conf import settings +logger = logging.getLogger(__name__) -wu_base_url = 'http://api.wunderground.com/api/{0:s}/'.format(settings.WEATHER_WEATHER_UNDERGROUND_API_KEY) -log = logging.getLogger('weather.lib') +def query_wttr_in(query): + """Hit the wttr.in JSON API with the provided query.""" + logger.info(f"about to query wttr.in with '{query}") + response = requests.get('http://wttr.in/{query}?format=j1') + response.raise_for_status() + + weather_info = response.json() + logger.debug(f"results: {weather_info}") + return weather_info -def get_conditions_for_query(queryitems): - """Make a wunderground conditions call, return as string.""" +def weather_summary(query): + """Create a more consumable version of the weather report.""" + weather_info = query_wttr_in(query) - # recombine the query into a string - query = ' '.join(queryitems) - query = query.replace(' ', '_') + # get some common/nested stuff once now + current = weather_info['current_condition'][0] + weather_desc = current['weatherDesc'][0] if 'weatherDesc' in current else {} + today_forecast = weather_info['weather'][0] + tomorrow_forecast = weather_info['weather'][1] + day_after_tomorrow_forecast = weather_info['weather'][2] - try: - url = wu_base_url + ('{0:s}/q/{1:s}.json'.format('conditions', query)) - log.debug("calling %s", url) - resp = requests.get(url) - condition_data = resp.json() - except IOError as e: - log.error("error while making conditions query") - log.exception(e) - raise + summary = { + 'location': query, + 'current': { + 'description': weather_desc.get('value'), + 'temp_C': f"{current.get('temp_C')}°C", + 'temp_F': f"{current.get('temp_F')}°F", + 'feels_like_temp_C': f"{current.get('FeelsLikeC')}°C", + 'feels_like_temp_F': f"{current.get('FeelsLikeF')}°F", + 'cloud_cover': f"{current.get('cloudcover')}%", + 'humidity': f"{current.get('humidity')}%", + 'precipitation': f"{current.get('precipMM')} mm", + 'visibility': f"{current.get('visibility')} mi", + 'wind_speed': f"{current.get('windspeedMiles')} MPH", + 'wind_direction': f"{current.get('winddir16Point')}", + 'pressure': f"{current.get('pressure')} mb", + 'uv_index': f"{current.get('uvIndex')}", + }, + } - # 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(condition_data) - - 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)) - resp = requests.get(url) - forecast_data = resp.json() - 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(forecast_data) - - 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) + return summary