Weather: rewrite to use weather underground

this is still a work in progress, but the basic support is there
This commit is contained in:
Brian S. Stephan 2012-09-17 16:26:29 -05:00
parent ded2b9e96e
commit 2699396dd8
1 changed files with 173 additions and 36 deletions

View File

@ -1,6 +1,6 @@
# coding: utf-8 # coding: utf-8
""" """
Weather - query various weather services for info Weather - query weather underground for info
Copyright (C) 2010 Brian S. Stephan Copyright (C) 2010 Brian S. Stephan
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -17,55 +17,192 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import json
import MySQLdb as mdb
import re import re
import urllib2
from extlib import pywapi
from urllib import quote
from urllib2 import URLError
from Module import Module from Module import Module
class Weather(Module): class Weather(Module):
"""Provide weather lookup services to the bot.""" """Provide weather lookup services to the bot."""
def do(self, connection, event, nick, userhost, what, admin_unlocked): def __init__(self, irc, config, server):
"""Query Google Weather for a location's weather.""" """Set up regexes and wunderground API."""
match = re.search('^!weather\s+(.*)$', what) Module.__init__(self, irc, config, server)
if match:
query = match.group(1) self.weatherre = re.compile("^!weather\s+(.*)$")
# get the API key from config
api_key = self._get_api_key_from_db()
if api_key is None:
raise Exception("weather underground API key not found!")
self.wu_base = 'http://api.wunderground.com/api/{0:s}/'.format(api_key)
def db_init(self):
"""Initialize the tiny table that stores the API key."""
version = self.db_module_registered(self.__class__.__name__)
if version == None:
db = self.get_db()
try: try:
google_weather = pywapi.get_weather_from_google(quote(query)) version = 1
city = google_weather['forecast_information']['city'].encode('utf-8') cur = db.cursor(mdb.cursors.DictCursor)
condition = google_weather['current_conditions']['condition'].encode('utf-8') cur.execute('''
temp_f = google_weather['current_conditions']['temp_f'].encode('utf-8') CREATE TABLE weather_settings (
temp_c = google_weather['current_conditions']['temp_c'].encode('utf-8') api_key VARCHAR(256) NOT NULL
wind = google_weather['current_conditions']['wind_condition'].encode('utf-8') )
humidity = google_weather['current_conditions']['humidity'].encode('utf-8') ''')
wind_speed_regex = re.compile("Wind: [NSEW]+ at ([0-9]+) mph") db.commit()
matches = wind_speed_regex.search(wind) self.db_register_module_version(self.__class__.__name__, version)
except mdb.Error as e:
db.rollback()
self.log.error("database error trying to create tables")
self.log.exception(e)
raise
finally: cur.close()
if matches is not None and float(temp_f) < 50.0: def do(self, connection, event, nick, userhost, what, admin_unlocked):
wind_speed = matches.group(1) """Handle IRC input for a weather queries."""
windchill = 35.74 + (0.6215 * float(temp_f)) - (35.75 * float(wind_speed)**0.16) + (0.4275 * float(temp_f) * float(wind_speed)**0.16)
else:
windchill = ""
weatherstr = "Current weather for " + city + ": " + condition + ". " + temp_f + "°F (" + temp_c + "°C), " + wind + ", " + humidity match = self.weatherre.search(what)
if windchill is not "": if match:
weatherstr += ", Wind chill: " + str(round(windchill, 2)) + "°F" # see if there was a specific command given in the query, first
weatherstr += "." query = match.group(1)
queryitems = query.split(" ")
if len(queryitems) <= 0:
return
for city in google_weather['forecasts']: # search for commands
weatherstr += " " + city['day_of_week'].encode('utf-8') + ": " + city['condition'].encode('utf-8') + ". High " + city['high'].encode('utf-8') + "°F, Low " + city['low'].encode('utf-8') + "°F." if queryitems[0] == "conditions":
# current weather query
results = self.get_conditions_for_query(queryitems[1:])
return self.reply(connection, event, results)
else:
# assume they wanted current weather
results = self.get_conditions_for_query(queryitems)
return self.reply(connection, event, results)
return self.reply(connection, event, weatherstr) def get_conditions_for_query(self, queryitems):
except URLError as e: """Make a wunderground conditions call, return as string."""
return self.reply(connection, event, "error connecting to google weather:" + str(e))
except IndexError as e: # recombine the query into a string
return self.reply(connection, event, "error in pywapi: " + str(e)) query = ' '.join(queryitems)
query = query.replace(' ', '_')
try:
url = self.wu_base + ('{0:s}/q/{1:s}.json'.format('conditions',
query))
json_resp = urllib2.urlopen(url)
condition_data = json.load(json_resp)
except IOError as e:
self.log.error("error while making conditions query")
self.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
self.log.debug(condition_data)
try:
# just see if we have current_observation data
current = condition_data['current_observation']
except KeyError as e:
self.log.error("error or bad query in conditions lookup")
self.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 reply.rstrip()
except KeyError as e:
self.log.error("error or unexpected results in conditions reply")
self.log.exception(e)
return "Error parsing results."
def _get_api_key_from_db(self):
"""Get the API key string from the database, or None if unset."""
api_key = None
db = self.get_db()
try:
cur = db.cursor(mdb.cursors.DictCursor)
query = '''SELECT api_key FROM weather_settings'''
cur.execute(query)
value = cur.fetchone()
if (value != None):
api_key = value['api_key']
except mdb.Error as e:
self.log.error("database error during api key retrieval")
self.log.exception(e)
finally: cur.close()
return api_key
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;