Weather: ported to new bot. deleted
This commit is contained in:
parent
41a7628c63
commit
1bd57a8c3b
|
@ -1,296 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
"""
|
|
||||||
Weather - query weather underground for info
|
|
||||||
Copyright (C) 2010 Brian S. Stephan
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
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
|
|
||||||
import urllib2
|
|
||||||
|
|
||||||
from Module import Module
|
|
||||||
|
|
||||||
class Weather(Module):
|
|
||||||
"""Provide weather lookup services to the bot."""
|
|
||||||
|
|
||||||
def __init__(self, irc, config):
|
|
||||||
"""Set up regexes and wunderground API."""
|
|
||||||
|
|
||||||
Module.__init__(self, irc, config)
|
|
||||||
|
|
||||||
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:
|
|
||||||
version = 1
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE weather_settings (
|
|
||||||
api_key VARCHAR(256) NOT NULL
|
|
||||||
)
|
|
||||||
''')
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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.irc.reply(event, results)
|
|
||||||
elif queryitems[0] == "forecast":
|
|
||||||
# forecast query
|
|
||||||
results = self.get_forecast_for_query(queryitems[1:])
|
|
||||||
return self.irc.reply(event, results)
|
|
||||||
else:
|
|
||||||
# assume they wanted current weather
|
|
||||||
results = self.get_conditions_for_query(queryitems)
|
|
||||||
return self.irc.reply(event, results)
|
|
||||||
|
|
||||||
def get_conditions_for_query(self, queryitems):
|
|
||||||
"""Make a wunderground conditions call, return as string."""
|
|
||||||
|
|
||||||
# recombine the query into a string
|
|
||||||
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(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
|
|
||||||
self.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
|
|
||||||
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 self._prettify_weather_strings(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_forecast_for_query(self, queryitems):
|
|
||||||
"""Make a wunderground forecast call, return as string."""
|
|
||||||
|
|
||||||
# recombine the query into a string
|
|
||||||
query = ' '.join(queryitems)
|
|
||||||
query = query.replace(' ', '_')
|
|
||||||
|
|
||||||
try:
|
|
||||||
url = self.wu_base + ('{0:s}/q/{1:s}.json'.format('forecast',
|
|
||||||
query))
|
|
||||||
json_resp = urllib2.urlopen(url)
|
|
||||||
forecast_data = json.load(json_resp)
|
|
||||||
except IOError as e:
|
|
||||||
self.log.error("error while making forecast query")
|
|
||||||
self.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
|
|
||||||
self.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
|
|
||||||
self.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
|
|
||||||
self.log.error("error or bad query in forecast lookup")
|
|
||||||
self.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 self._prettify_weather_strings(reply.rstrip())
|
|
||||||
except KeyError as e:
|
|
||||||
self.log.error("error or unexpected results in forecast 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
|
|
||||||
|
|
||||||
def _prettify_weather_strings(self, 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)
|
|
||||||
|
|
||||||
# vi:tabstop=4:expandtab:autoindent
|
|
||||||
# kate: indent-mode python;indent-width 4;replace-tabs on;
|
|
Loading…
Reference in New Issue