Weather: rewrite to use weather underground
this is still a work in progress, but the basic support is there
This commit is contained in:
		
							parent
							
								
									ded2b9e96e
								
							
						
					
					
						commit
						2699396dd8
					
				@ -1,6 +1,6 @@
 | 
			
		||||
# coding: utf-8
 | 
			
		||||
"""
 | 
			
		||||
Weather - query various weather services for info
 | 
			
		||||
Weather - query weather underground for info
 | 
			
		||||
Copyright (C) 2010  Brian S. Stephan
 | 
			
		||||
 | 
			
		||||
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/>.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import MySQLdb as mdb
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from extlib import pywapi
 | 
			
		||||
 | 
			
		||||
from urllib import quote
 | 
			
		||||
from urllib2 import URLError
 | 
			
		||||
import urllib2
 | 
			
		||||
 | 
			
		||||
from Module import Module
 | 
			
		||||
 | 
			
		||||
class Weather(Module):
 | 
			
		||||
    """Provide weather lookup services to the bot."""
 | 
			
		||||
 | 
			
		||||
    def do(self, connection, event, nick, userhost, what, admin_unlocked):
 | 
			
		||||
        """Query Google Weather for a location's weather."""
 | 
			
		||||
    def __init__(self, irc, config, server):
 | 
			
		||||
        """Set up regexes and wunderground API."""
 | 
			
		||||
 | 
			
		||||
        match = re.search('^!weather\s+(.*)$', what)
 | 
			
		||||
        if match:
 | 
			
		||||
            query = match.group(1)
 | 
			
		||||
        Module.__init__(self, irc, config, server)
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
                google_weather = pywapi.get_weather_from_google(quote(query))
 | 
			
		||||
                city = google_weather['forecast_information']['city'].encode('utf-8')
 | 
			
		||||
                condition = google_weather['current_conditions']['condition'].encode('utf-8')
 | 
			
		||||
                temp_f = google_weather['current_conditions']['temp_f'].encode('utf-8')
 | 
			
		||||
                temp_c = google_weather['current_conditions']['temp_c'].encode('utf-8')
 | 
			
		||||
                wind = google_weather['current_conditions']['wind_condition'].encode('utf-8')
 | 
			
		||||
                humidity = google_weather['current_conditions']['humidity'].encode('utf-8')
 | 
			
		||||
                version = 1
 | 
			
		||||
                cur = db.cursor(mdb.cursors.DictCursor)
 | 
			
		||||
                cur.execute('''
 | 
			
		||||
                    CREATE TABLE weather_settings (
 | 
			
		||||
                        api_key VARCHAR(256) NOT NULL
 | 
			
		||||
                    )
 | 
			
		||||
                    ''')
 | 
			
		||||
 | 
			
		||||
                wind_speed_regex = re.compile("Wind: [NSEW]+ at ([0-9]+) mph")
 | 
			
		||||
                matches = wind_speed_regex.search(wind)
 | 
			
		||||
                db.commit()
 | 
			
		||||
                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:
 | 
			
		||||
                    wind_speed = matches.group(1)
 | 
			
		||||
                    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)
 | 
			
		||||
    def do(self, connection, event, nick, userhost, what, admin_unlocked):
 | 
			
		||||
        """Handle IRC input for a weather queries."""
 | 
			
		||||
 | 
			
		||||
        match = self.weatherre.search(what)
 | 
			
		||||
        if match:
 | 
			
		||||
            # 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 = self.get_conditions_for_query(queryitems[1:])
 | 
			
		||||
                return self.reply(connection, event, results)
 | 
			
		||||
            else:
 | 
			
		||||
                    windchill = ""
 | 
			
		||||
                # assume they wanted current weather
 | 
			
		||||
                results = self.get_conditions_for_query(queryitems)
 | 
			
		||||
                return self.reply(connection, event, results)
 | 
			
		||||
 | 
			
		||||
                weatherstr = "Current weather for " + city + ": " + condition + ". " + temp_f + "°F (" + temp_c + "°C), " + wind + ", " + humidity
 | 
			
		||||
                if windchill is not "":
 | 
			
		||||
                    weatherstr += ", Wind chill: " + str(round(windchill, 2)) + "°F"
 | 
			
		||||
                weatherstr += "."
 | 
			
		||||
    def get_conditions_for_query(self, queryitems):
 | 
			
		||||
        """Make a wunderground conditions call, return as string."""
 | 
			
		||||
 | 
			
		||||
                for city in google_weather['forecasts']:
 | 
			
		||||
                    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."
 | 
			
		||||
        # recombine the query into a string
 | 
			
		||||
        query = ' '.join(queryitems)
 | 
			
		||||
        query = query.replace(' ', '_')
 | 
			
		||||
 | 
			
		||||
                return self.reply(connection, event, weatherstr)
 | 
			
		||||
            except URLError as e:
 | 
			
		||||
                return self.reply(connection, event, "error connecting to google weather:" + str(e))
 | 
			
		||||
            except IndexError as e:
 | 
			
		||||
                return self.reply(connection, event, "error in pywapi: " + str(e))
 | 
			
		||||
        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
 | 
			
		||||
# kate: indent-mode python;indent-width 4;replace-tabs on;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user