Rewriting parts of the plugin... part 1

- showImperialandMetric is dropped and now implied
- optlist handling is completely removed since it made already complex code even more so
- Lots of bugs introduced hopefully!
This commit is contained in:
James Lu 2015-03-11 14:57:33 -07:00
parent bea95b27c3
commit 599d40f19f
2 changed files with 144 additions and 314 deletions

View File

@ -23,7 +23,7 @@ conf.registerGlobalValue(Weather, 'apiKey',
conf.registerChannelValue(Weather, 'useImperial', conf.registerChannelValue(Weather, 'useImperial',
registry.Boolean(True, ("""Determines whether imperial units (Fahrenheit, etc.) will be used."""))) registry.Boolean(True, ("""Determines whether imperial units (Fahrenheit, etc.) will be used.""")))
conf.registerChannelValue(Weather, 'disableColoredTemp', conf.registerChannelValue(Weather, 'disableColoredTemp',
registry.Boolean(False, """Determines whether colors will be used for displaying temperatures."""))) registry.Boolean(False, ("""Determines whether colors will be used for displaying temperatures.""")))
conf.registerGlobalValue(Weather,'forecast', conf.registerGlobalValue(Weather,'forecast',
registry.Boolean(True, ("""Determines whether forecasts will be displayed by default."""))) registry.Boolean(True, ("""Determines whether forecasts will be displayed by default.""")))
conf.registerGlobalValue(Weather,'alerts', conf.registerGlobalValue(Weather,'alerts',
@ -38,8 +38,6 @@ conf.registerGlobalValue(Weather, 'showWind',
registry.Boolean(False, ("""Determines whether winde will be displayed by default."""))) registry.Boolean(False, ("""Determines whether winde will be displayed by default.""")))
conf.registerGlobalValue(Weather, 'showUpdated', conf.registerGlobalValue(Weather, 'showUpdated',
registry.Boolean(False, ("""Determines whether the bot will show the data's "last updated" time by default."""))) registry.Boolean(False, ("""Determines whether the bot will show the data's "last updated" time by default.""")))
conf.registerChannelValue(Weather,'showImperialAndMetric',
registry.Boolean(True, ("""Determines whether both imperial and metric units will shown where applicable.""")))
conf.registerGlobalValue(Weather, 'lang', conf.registerGlobalValue(Weather, 'lang',
registry.String('EN', ("""Determines the language used by the plugin."""))) registry.String('EN', ("""Determines the language used by the plugin.""")))

454
plugin.py
View File

@ -129,8 +129,7 @@ class WeatherDB():
class Weather(callbacks.Plugin): class Weather(callbacks.Plugin):
"""Add the help for "@plugin help Weather" here """This plugin provides access to information from Weather Underground."""
This should describe *how* to use this plugin."""
threaded = True threaded = True
def __init__(self, irc): def __init__(self, irc):
@ -139,9 +138,6 @@ class Weather(callbacks.Plugin):
self.APIKEY = self.registryValue('apiKey') self.APIKEY = self.registryValue('apiKey')
self.db = WeatherDB() self.db = WeatherDB()
def die(self):
self.__parent.die()
############## ##############
# FORMATTING # # FORMATTING #
############## ##############
@ -152,9 +148,6 @@ class Weather(callbacks.Plugin):
def _bu(self, string): def _bu(self, string):
return ircutils.underline(ircutils.bold(string)) return ircutils.underline(ircutils.bold(string))
def _strip(self, string):
return ircutils.stripFormatting(string)
############################ ############################
# INTERNAL WEATHER HELPERS # # INTERNAL WEATHER HELPERS #
############################ ############################
@ -162,24 +155,24 @@ class Weather(callbacks.Plugin):
def _weatherSymbol(self, code): def _weatherSymbol(self, code):
"""Return a unicode symbol based on weather status.""" """Return a unicode symbol based on weather status."""
table = {'partlycloudy':'~☁', table = {'partlycloudy': '~☁',
'cloudy':'', 'cloudy': '',
'tstorms':'', 'tstorms': '',
'sunny':'', 'sunny': '',
'snow':'', 'snow': '',
'sleet':'', 'sleet': '',
'rain':'', 'rain': '',
'mostlysunny':'~☀', 'mostlysunny': '~☀',
'mostlycloudy':'~☁', 'mostlycloudy': '~☁',
'hazy':'', 'hazy': '',
'fog':'', 'fog': '',
'flurries':'', 'flurries': '',
'clear':'', 'clear': '',
'chanceflurries':'?❄', 'chanceflurries': '?❄',
'chancerain':'?☔', 'chancerain': '?☔',
'chancesleet':'?❄', 'chancesleet': '?❄',
'chancesnow':'?❄', 'chancesnow': '?❄',
'chancetstorms':'?☔' } 'chancetstorms': '?☔'}
# return symbol from table. # return symbol from table.
try: try:
return table[code] return table[code]
@ -209,58 +202,40 @@ class Weather(callbacks.Plugin):
# return. # return.
return symbol return symbol
def _temp(self, x): def _temp(self, f, c=None):
"""Returns a colored string based on the temperature.""" """Returns a colored string based on the temperature."""
# lets be safe and wrap in a try/except because we can't always trust data purity. # lets be safe and wrap in a try/except because we can't always trust data purity.
try: try:
if x.startswith('NA'): # Wunderground sends a field that's not available if str(f).startswith('NA'): # Wunderground sends a field that's not available
return x return f
# first, convert into F so we only have one table. f = int(f)
if x.endswith('C'): # c. if not c:
x = float(x[:-1]) * 9 / 5 + 32 # remove C + math into float(F). c = int((f - 32) * 5/9)
unit = "C"
else: # f.
x = float(x[:-1]) # remove F. str->float.
unit = "F"
# determine color. # determine color.
if x < 10.0: if f < 10.0:
color = 'light blue' color = 'light blue'
elif 10.0 <= x <= 32.0: elif 10.0 <= f <= 32.0:
color = 'teal' color = 'teal'
elif 32.1 <= x <= 50.0: elif 32.1 <= f <= 50.0:
color = 'blue' color = 'blue'
elif 50.1 <= x <= 60.0: elif 50.1 <= f <= 60.0:
color = 'light green' color = 'light green'
elif 60.1 <= x <= 70.0: elif 60.1 <= f <= 70.0:
color = 'green' color = 'green'
elif 70.1 <= x <= 80.0: elif 70.1 <= f <= 80.0:
color = 'yellow' color = 'yellow'
elif 80.1 <= x <= 90.0: elif 80.1 <= f <= 90.0:
color = 'orange' color = 'orange'
elif x > 90.0: elif f > 90.0:
color = 'red' color = 'red'
else: else:
color = 'light grey' color = 'light grey'
# return. # return.
if unit == "F": # no need to convert back. return ircutils.mircColor(("{0}F/{1}C".format(f, c)), color)
return ircutils.mircColor(("{0:.0f}F".format(x)), color)
else: # temp is in F and we need to go back to C.
return ircutils.mircColor(("{0:.0f}C".format((x - 32) * 5 / 9)),color)
except Exception as e: # rutroh. something went wrong. except Exception as e: # rutroh. something went wrong.
self.log.info("_temp: ERROR trying to convert temp: {0} message: {1}".format(x, e)) self.log.info("Weather: Error trying to convert temp: {0} message: {1}".format(f, e))
return x return f
def _tw(self, bol, x):
"""This is a convenience handle that wraps _temp."""
# make sure we have 'bol', which should come in from args['nocolortemp'].
# since the option is a negation, we assume NO.
if not bol: # COLOR IT.
x = self._temp(x)
return x
else:
return x
def _wind(self, angle, useSymbols=False): def _wind(self, angle, useSymbols=False):
"""Converts degrees to direction for wind. Can optionally return a symbol.""" """Converts degrees to direction for wind. Can optionally return a symbol."""
@ -294,19 +269,18 @@ class Weather(callbacks.Plugin):
# grab a list of valid settings. # grab a list of valid settings.
validset = self.db.getsettings() validset = self.db.getsettings()
if optset not in validset: if optset not in validset:
irc.error("'{0}' is an invalid setting. Must be one of: {1}".format(optset, " | ".join(sorted([i for i in validset]))), Raise=True) irc.error(format("%r is an invalid setting. Must be one of: %L.", optset,
return sorted(validset)), Raise=True)
# setting value True/False
if optbool: # True. if optbool: # True.
value = 1 value = 1
else: # False. else: # False.
value = 0 value = 0
# check user first. # check user first.
if not self.db.getuser(msg.nick.lower()): # user exists if not self.db.getuser(msg.nick.lower()): # user exists
irc.error("You're not in the database. You must setweather first.", Raise=True) irc.error("You are not in the database; you must use 'setweather' first.", Raise=True)
else: # user is valid. perform the op. else: # user is valid. perform the op.
self.db.setsetting(msg.nick.lower(), optset, value) self.db.setsetting(msg.nick.lower(), optset, value)
irc.reply("I have changed {0}'s {1} setting to {2}".format(msg.nick, optset, value)) irc.replySuccess()
setuser = wrap(setuser, [('somethingWithoutSpaces'), ('boolean')]) setuser = wrap(setuser, [('somethingWithoutSpaces'), ('boolean')])
@ -317,10 +291,8 @@ class Weather(callbacks.Plugin):
Use your zip/postal code to keep it simple. Use your zip/postal code to keep it simple.
Ex: setweather 10012 Ex: setweather 10012
""" """
# set the weather id based on nick. This will update or set.
self.db.setweather(msg.nick.lower(), optlocation) self.db.setweather(msg.nick.lower(), optlocation)
irc.reply("I have changed {0}'s weather ID to {1}".format(msg.nick.lower(), optlocation)) irc.replySuccess()
setweather = wrap(setweather, [('text')]) setweather = wrap(setweather, [('text')])
@ -332,28 +304,33 @@ class Weather(callbacks.Plugin):
"""Internal helper to find a location via Wunderground's autocomplete API.""" """Internal helper to find a location via Wunderground's autocomplete API."""
url = 'http://autocomplete.wunderground.com/aq?query=%s' % utils.web.urlquote(q) url = 'http://autocomplete.wunderground.com/aq?query=%s' % utils.web.urlquote(q)
#self.log.info("WUAC URL: {0}".format(url)) self.log.debug("Autocomplete URL: %s", url)
# try and fetch.
try: try:
page = utils.web.getUrl(url) page = utils.web.getUrl(url)
except Exception as e: # something didn't work. except Exception as e:
self.log.info("_wuac: ERROR: Trying to open {0} message: {1}".format(url, e)) self.log.info("Weather: (_wuac) Error trying to open {0} message: {1}".format(url, e))
return None return None
# now process json and return.
try: try:
data = json.loads(page.decode('utf-8')) data = json.loads(page.decode('utf-8'))
loc = data['RESULTS'][0]['zmw'] # find the first zmw. # ZMW is in some ways a lot like Wunderground's location ID Codes, for when locations
loc = "zmw:%s" % loc # return w/zmw: attached. # are too ambiguous. (e.g. looking up "France", which is a country with many different
# locations!)
for item in data['RESULTS']:
# Sometimes the autocomplete will lead us to more disambiguation pages...
# which cause lots of errors in processing!
if item['tz'] != 'MISSING':
loc = "zmw:%s" % item['zmw']
break
return loc return loc
except Exception as e: except Exception as e:
self.log.info("_wuac: ERROR processing json in {0} :: {1}".format(url, e)) self.log.info("Weather: (_wuac) Error processing JSON in {0} :: {1}".format(url, e))
return None return None
def _wunderjson(self, url, location): def _wunderjson(self, url, location):
"""Fetch wunderground JSON and return.""" """Fetch wunderground JSON and return."""
# first, construct the url properly. # first, construct the url properly.
if url.endswith('/'): # cheap way to strip the tailing / if url.endswith('/'): # cheap way to strip the trailing /
url = '%sq/%s.json' % (url, utils.web.urlquote(location)) url = '%sq/%s.json' % (url, utils.web.urlquote(location))
else: else:
url = '%s/q/%s.json' % (url, utils.web.urlquote(location)) url = '%s/q/%s.json' % (url, utils.web.urlquote(location))
@ -363,184 +340,98 @@ class Weather(callbacks.Plugin):
page = utils.web.getUrl(url) page = utils.web.getUrl(url)
return page return page
except Exception as e: # something didn't work. except Exception as e: # something didn't work.
self.log.info("_wunderjson: ERROR Trying to open {0} message: {1}".format(url, e)) self.log.info("Weather: (_wunderjson) Error trying to open {0} message: {1}".format(url, e))
return None return None
#################### ####################
# PUBLIC FUNCTIONS # # PUBLIC FUNCTIONS #
#################### ####################
def wunderground(self, irc, msg, args, optlist, optinput): def wunderground(self, irc, msg, args, optinput):
"""[--options] <location> """[--options] <location>
Fetch weather and forcast information for <location> Fetches weather and forecast information for <location>.
Location must be one of: US state/city (CA/San_Francisco), zipcode, country/city (Australia/Sydney), airport code (KJFK) Location must be one of: US state/city (CA/San_Francisco), zip code, country/city (Australia/Sydney), or an airport code (KJFK).
Use --help to list all options.
Ex: 10021 or Sydney, Australia or KJFK Ex: 10021 or Sydney, Australia or KJFK
""" """
# first, check if we have an API key. Useless w/o this. if not self.APIKEY:
if len(self.APIKEY) < 1 or not self.APIKEY or self.APIKEY == "Not set": irc.error("No Wunderground API key was defined. Set 'config plugins.Weather.apiKey' and reload the plugin.",
irc.error("Need a Wunderground API key. Set config plugins.Weather.apiKey and reload Weather.", Raise=True) Raise=True)
# urlargs will be used to build the url to query the API. # urlargs will be used to build the url to query the API.
# besides lang, these are unmutable values that should not be changed. # besides lang, these are unmutable values that should not be changed.
urlArgs = {'features':['conditions', 'forecast'], urlArgs = {'features': ['conditions', 'forecast'],
'lang':self.registryValue('lang'), 'lang': self.registryValue('lang'),
'bestfct':'1', 'bestfct': '1',
'pws':'0' } 'pws': '0' }
# now, figure out the rest of the options for fetching and displaying weather.
# some of these are for the query and the others are for output.
# the order will always go global->channel (supybot config) -> user.
loc = None loc = None
args = {'imperial':self.registryValue('useImperial', msg.args[0]), args = {'imperial': self.registryValue('useImperial', msg.args[0]),
'nocolortemp':self.registryValue('disableColoredTemp', msg.args[0]), 'nocolortemp': self.registryValue('disableColoredTemp', msg.args[0]),
'alerts':self.registryValue('alerts'), 'alerts': self.registryValue('alerts'),
'almanac':self.registryValue('almanac'), 'almanac': self.registryValue('almanac'),
'astronomy':self.registryValue('astronomy'), 'astronomy': self.registryValue('astronomy'),
'pressure':self.registryValue('showPressure'), 'pressure': self.registryValue('showPressure'),
'wind':self.registryValue('showWind'), 'wind': self.registryValue('showWind'),
'updated':self.registryValue('showUpdated'), 'updated': self.registryValue('showUpdated'),
'showImperialAndMetric':self.registryValue('showImperialAndMetric', msg.args[0]), 'forecast': False,
'forecast':False, 'humidity': False,
'humidity':False, 'uv': False,
'strip':False, 'visibility': False,
'uv':False, 'dewpoint': False}
'visibility':False,
'dewpoint':False }
# instead of doing optlist, we need to handle the location/options to set initially. usersetting = self.db.getweather(msg.nick.lower())
# first, check if there is a user so we can grab their settings. if usersetting:
usersetting = self.db.getweather(msg.nick.lower()) # check the db. for (k, v) in usersetting.items():
if usersetting: # user is found. lets grab their location and settings. args[k] = v
for (k, v) in usersetting.items(): # iterate over settings dict returned from getweather row. loc = usersetting["location"]
# set specific settings based on keys that won't 1:1 match. args['imperial'] = (not usersetting["metric"])
if k == 'location': # location. look down below this for how the logic is handled. args['nocolortemp'] = (not usersetting["colortemp"])
loc = v # copy over their location from the DB to loc. else:
elif k == 'metric': # metric
if v == 1: # true.
args['imperial'] = False
else: # 0 = false.
args['imperial'] = True
elif k == 'colortemp': # colortemp.
if v == 1: # true.
args['nocolortemp'] = False
else: # false. the 'nocolortemp' values are inverse.
args['nocolortemp'] = True
else: # rest of them are 1:1.
if v == 1: # if value is 1, or true.
args[k] = True
else: # argument is 0 or False.
args[k] = False
else: # user was not found.
if not optinput: # location was also not specified, so we must bail. if not optinput: # location was also not specified, so we must bail.
irc.error("I did not find a preset location for you. Set via setweather <location>", Raise=True) irc.error("I did not find a preset location for you. Set one via 'setweather <location>'.", Raise=True)
# handle optlist (getopts). this will manipulate output via args dict. if optinput:
# we must do this after the dblookup for users as it would always override.
if optlist:
for (key, value) in optlist:
if key == "metric":
args['imperial'] = False
if key == 'alerts':
args['alerts'] = True
if key == 'forecast':
args['forecast'] = True
if key == 'almanac':
args['almanac'] = True
if key == 'pressure':
args['pressure'] = True
if key == 'humidity':
args['humidity'] = True
if key == 'wind':
args['wind'] = True
if key == 'uv':
args['uv'] = True
if key == 'visibility':
args['visibility'] = True
if key == 'dewpoint':
args['dewpoint'] = True
if key == 'astronomy':
args['astronomy'] = True
if key == 'nocolortemp':
args['nocolortemp'] = True
if key == 'help': # make shift help because the docstring is overloaded above.
irc.reply("Options: --metric --alerts --forecast --almanac --pressure --wind --uv --visibility --dewpoint --astronomy --nocolortemp")
irc.reply("WeatherDB options: setweather <location> (set user's location). setmetric True/False (set metric option) setcolortemp True/False (display color temp?")
return
# now that we're done with 'input things'
# we need to decide on how to handle the location.
# optinput = user specified location, regardless if they're known or not.
# loc = the location that can come back if a user is known and this is set.
# both of these might not be valid locations. however, if a user specifies a location, we should look it up.
if optinput: # if we have optinput, regardless if the user is known or not, autocomplete it.
wloc = self._wuac(optinput) wloc = self._wuac(optinput)
if not wloc: # error looking up the location. if not wloc:
irc.error("I could not find a valid location for: {0}".format(optinput), Raise=True) irc.error("I could not find a valid location for: {0}".format(optinput), Raise=True)
elif loc and not optinput: # user is known. location is set. no optinput. elif loc: # user is known. location is set. no optinput.
wloc = loc # set wloc as their location. worst case, the user gets an error for setting it wrong. wloc = loc
else: # no optinput. no location. error out. this should happen above but lets be redundant. else: # no optinput. no location. error out. this should happen above but lets be redundant.
irc.error("You must specify a city to search for weather.", Raise=True) irc.error("You must specify a city to search for weather.", Raise=True)
# build url now. first, apikey. then, iterate over urlArgs and insert. url = 'http://api.wunderground.com/api/%s/' % (self.APIKEY)
url = 'http://api.wunderground.com/api/%s/' % (self.APIKEY) # first part of url, w/APIKEY
# now we need to set certain things for urlArgs based on args.
for check in ['alerts', 'almanac', 'astronomy']: for check in ['alerts', 'almanac', 'astronomy']:
if args[check]: # if args['value'] is True, either via config or getopts. if args[check]:
urlArgs['features'].append(check) # append to dict->key (list) urlArgs['features'].append(check) # append to dict->key (list)
# now, we use urlArgs dict to append to url. # now, we use urlArgs dict to append to url.
for (key, value) in urlArgs.items(): for (key, value) in urlArgs.items():
if key == "features": # will always be at least conditions. if key == "features": # will always be at least conditions.
url += "".join([item + '/' for item in value]) # listcmp the features/ url += "".join([item + '/' for item in value]) # listcmp the features/
if key == "lang" or key == "bestfct" or key == "pws": # rest added with key:value if key in ("lang", "bestfct", "pws"): # rest added with key:value
url += "{0}:{1}/".format(key, value) url += "{0}:{1}/".format(key, value)
# now that we're done, lets finally make our API call.
page = self._wunderjson(url, wloc) page = self._wunderjson(url, wloc)
if not page:
irc.error("Failed to load Wunderground API. Check the logs for more information.", Raise=True)
# process json.
try: try:
data = json.loads(page.decode('utf-8')) data = json.loads(page.decode('utf-8'))
except Exception as e: except Exception as e:
self.log.error("ERROR: could not process JSON from: {0} :: {1}".format(url, e)) self.log.error("Weather: Error processing JSON from: {0} :: {1}".format(url, e))
irc.error("Could not process JSON from Weather Underground. Check the logs.", Raise=True) irc.error("Could not process JSON from Weather Underground. Check the logs.", Raise=True)
# now, a series of sanity checks before we process. outdata = {'weather': data['current_observation']['weather'],
if 'error' in data['response']: # check if there are errors. 'location': data['current_observation']['display_location']['full'],
errortype = data['response']['error']['type'] # type. description is below. 'humidity': data['current_observation']['relative_humidity'],
errordesc = data['response']['error'].get('description', 'no description') 'uv': data['current_observation']['UV']}
irc.error("I got an error searching '{0}'. ({1}: {2})".format(loc, errortype, errordesc), Raise=True)
# if there is more than one city matching (Ambiguous Results). we now go with the first (best?) match.
# this should no longer be the case with our autocomplete routine above but we'll keep this anyways.
if 'results' in data['response']: # we grab the first location's "ZMW" which then gets constructed as location.
first = 'zmw:%s' % data['response']['results'][0]['zmw'] # grab the "first" location and create the
# grab this first location and search again.
page = self._wunderjson(url, first)
if not page:
irc.error("Failed to load Wunderground API.", Raise=True)
# we're here if we got the second search (best?) now lets reload the json and continue.
data = json.loads(page.decode('utf-8'))
# no errors so we start the main part of processing.
outdata = {}
outdata['weather'] = data['current_observation']['weather']
outdata['location'] = data['current_observation']['display_location']['full']
outdata['humidity'] = data['current_observation']['relative_humidity']
outdata['uv'] = data['current_observation']['UV']
# handle wind. check if there is none first.
if data['current_observation']['wind_mph'] < 1: # no wind. if data['current_observation']['wind_mph'] < 1: # no wind.
outdata['wind'] = "None" outdata['wind'] = "None"
else: # we do have wind. process differently. else:
if args['imperial']: # imperial units for wind. if args['imperial']:
outdata['wind'] = "{0}@{1}mph".format(self._wind(data['current_observation']['wind_degrees']), data['current_observation']['wind_mph']) outdata['wind'] = "{0}@{1}mph".format(self._wind(data['current_observation']['wind_degrees']), data['current_observation']['wind_mph'])
if int(data['current_observation']['wind_gust_mph']) > 0: # gusts? if int(data['current_observation']['wind_gust_mph']) > 0: # gusts?
outdata['wind'] += " ({0}mph gusts)".format(data['current_observation']['wind_gust_mph']) outdata['wind'] += " ({0}mph gusts)".format(data['current_observation']['wind_gust_mph'])
else: # handle metric units for wind. else:
outdata['wind'] = "{0}@{1}kph".format(self._wind(data['current_observation']['wind_degrees']),data['current_observation']['wind_kph']) outdata['wind'] = "{0}@{1}kph".format(self._wind(data['current_observation']['wind_degrees']),data['current_observation']['wind_kph'])
if int(data['current_observation']['wind_gust_kph']) > 0: # gusts? if int(data['current_observation']['wind_gust_kph']) > 0: # gusts?
outdata['wind'] += " ({0}kph gusts)".format(data['current_observation']['wind_gust_kph']) outdata['wind'] += " ({0}kph gusts)".format(data['current_observation']['wind_gust_kph'])
@ -565,106 +456,59 @@ class Weather(callbacks.Plugin):
outdata['observation'] = '1hr ago' outdata['observation'] = '1hr ago'
else: else:
outdata['observation'] = '{0}hrs ago'.format(s/3600) outdata['observation'] = '{0}hrs ago'.format(s/3600)
outdata['temp'] = self._temp(data['current_observation']['temp_f'])
# handle basics like temp/pressure/dewpoint. big conditional here # pressure.
# as we can display Imperial + Metric, or one or the other. pin = str(data['current_observation']['pressure_in']) + 'in'
if args['showImperialAndMetric']: pmb = str(data['current_observation']['pressure_mb']) + 'mb'
# lets put C and F into strings to make it easier. outdata['pressure'] = "{0}/{1}".format(pin, pmb)
tf = str(data['current_observation']['temp_f']) + 'F' # dewpoint.
tc = str(data['current_observation']['temp_c']) + 'C' outdata['dewpoint'] = self._temp(data['current_observation']['dewpoint_f'])
outdata['temp'] = "{0}/{1}".format(self._tw(args['nocolortemp'], tf), self._tw(args['nocolortemp'], tc)) # heatindex.
# now lets do pressure. outdata['heatindex'] = self._temp(data['current_observation']['heat_index_f'])
pin = str(data['current_observation']['pressure_in']) + 'in' # windchill.
pmb = str(data['current_observation']['pressure_mb']) + 'mb' outdata['windchill'] = self._temp(data['current_observation']['windchill_f'])
outdata['pressure'] = "{0}/{1}".format(pin, pmb) # feels like
# dewpoint. outdata['feelslike'] = self._temp(data['current_observation']['feelslike_f'])
dpf = str(data['current_observation']['dewpoint_f']) + 'F' # visibility.
dpc = str(data['current_observation']['dewpoint_c']) + 'C' vmi = str(data['current_observation']['visibility_mi']) + 'mi'
outdata['dewpoint'] = "{0}/{1}".format(self._tw(args['nocolortemp'], dpf), self._tw(args['nocolortemp'], dpc)) vkm = str(data['current_observation']['visibility_km']) + 'km'
# heatindex. outdata['visibility'] = "{0}/{1}".format(vmi, vkm)
hif = str(data['current_observation']['heat_index_f']) + 'F'
hic = str(data['current_observation']['heat_index_c']) + 'C'
outdata['heatindex'] = "{0}/{1}".format(self._tw(args['nocolortemp'], hif), self._tw(args['nocolortemp'], hic))
# windchill.
wcf = str(data['current_observation']['windchill_f']) + 'F'
wcc = str(data['current_observation']['windchill_c']) + 'C'
outdata['windchill'] = "{0}/{1}".format(self._tw(args['nocolortemp'], wcf), self._tw(args['nocolortemp'], wcc))
# feels like
flf = str(data['current_observation']['feelslike_f']) + 'F'
flc = str(data['current_observation']['feelslike_c']) + 'C'
outdata['feelslike'] = "{0}/{1}".format(self._tw(args['nocolortemp'], flf), self._tw(args['nocolortemp'], flc))
# visibility.
vmi = str(data['current_observation']['visibility_mi']) + 'mi'
vkm = str(data['current_observation']['visibility_km']) + 'km'
outdata['visibility'] = "{0}/{1}".format(vmi, vkm)
else: # don't display both (default)
if args['imperial']: # assigns the symbol based on metric.
outdata['temp'] = self._tw(args['nocolortemp'], str(data['current_observation']['temp_f']) + 'F')
outdata['pressure'] = str(data['current_observation']['pressure_in']) + 'in'
outdata['dewpoint'] = self._tw(args['nocolortemp'], str(data['current_observation']['dewpoint_f']) + 'F')
outdata['heatindex'] = self._tw(args['nocolortemp'], str(data['current_observation']['heat_index_f']) + 'F')
outdata['windchill'] = self._tw(args['nocolortemp'], str(data['current_observation']['windchill_f']) + 'F')
outdata['feelslike'] = self._tw(args['nocolortemp'], str(data['current_observation']['feelslike_f']) + 'F')
outdata['visibility'] = str(data['current_observation']['visibility_mi']) + 'mi'
else: # metric.
outdata['temp'] = self._tw(args['nocolortemp'], str(data['current_observation']['temp_c']) + 'C')
outdata['pressure'] = str(data['current_observation']['pressure_mb']) + 'mb'
outdata['dewpoint'] = self._tw(args['nocolortemp'], str(data['current_observation']['dewpoint_c']) + 'C')
outdata['heatindex'] = self._tw(args['nocolortemp'], str(data['current_observation']['heat_index_c']) + 'C')
outdata['windchill'] = self._tw(args['nocolortemp'], str(data['current_observation']['windchill_c']) + 'C')
outdata['feelslike'] = self._tw(args['nocolortemp'], str(data['current_observation']['feelslike_c']) + 'C')
outdata['visibility'] = str(data['current_observation']['visibility_km']) + 'km'
# handle forecast data part. output will be below. (not --forecast) # handle forecast data part. output will be below. (not --forecast)
forecastdata = {} # key = int(day), value = forecast dict. forecastdata = {} # key = int(day), value = forecast dict.
for forecastday in data['forecast']['txt_forecast']['forecastday']: for forecastday in data['forecast']['txt_forecast']['forecastday']:
tmpdict = {} if args['imperial']:
tmpdict['day'] = forecastday['title'] text = forecastday['fcttext']
# tmpdict['symbol'] = self._weatherSymbol(forecastday['icon']) else:
if args['imperial']: # imperial. text = forecastday['fcttext_metric']
tmpdict['text'] = forecastday['fcttext'] forecastdata[int(forecastday['period'])] = {'day': forecastday['title'],
else: # metric. 'text': text}
tmpdict['text'] = forecastday['fcttext_metric']
forecastdata[int(forecastday['period'])] = tmpdict
# now this is the --forecast part. # now this is the --forecast part.
if args['forecast']: # only if we get this in getopts. if args['forecast']:
fullforecastdata = {} # key = day (int), value = dict of forecast data. fullforecastdata = {} # key = day (int), value = dict of forecast data.
for forecastday in data['forecast']['simpleforecast']['forecastday']: for forecastday in data['forecast']['simpleforecast']['forecastday']:
tmpdict = {} high = self._temp(forecastday['high']['fahrenheit'])
tmpdict['day'] = forecastday['date']['weekday_short'] low = self._temp(forecastday['low']['fahrenheit'])
tmpdict['symbol'] = self._weatherSymbol(forecastday['icon']) tmpdict = {'day': forecastday['date']['weekday_short'],
tmpdict['text'] = forecastday['conditions'] 'symbol': self._weatherSymbol(forecastday['icon']),
if args['imperial']: # imperial. 'text': forecastday['conditions'],
tmpdict['high'] = forecastday['high']['fahrenheit'] + "F" 'low': low,
tmpdict['low'] = forecastday['low']['fahrenheit'] + "F" 'high': high}
else: # metric.
tmpdict['high'] = forecastday['high']['celsius'] + "C"
tmpdict['low'] = forecastday['low']['celsius'] + "C"
fullforecastdata[int(forecastday['period'])] = tmpdict fullforecastdata[int(forecastday['period'])] = tmpdict
# handle almanac # handle almanac
if args['almanac']: if args['almanac']:
outdata['highyear'] = data['almanac']['temp_high'].get('recordyear', 'NA') outdata['highyear'] = data['almanac']['temp_high'].get('recordyear', 'NA')
outdata['lowyear'] = data['almanac']['temp_low'].get('recordyear', 'NA') outdata['lowyear'] = data['almanac']['temp_low'].get('recordyear', 'NA')
if args['imperial']: # imperial. outdata['highnormal'] = self._temp(data['almanac']['temp_high']['normal']['F'])
outdata['highnormal'] = data['almanac']['temp_high']['normal']['F'] + "F" outdata['highlow'] = self._temp(data['almanac']['temp_low']['normal']['F'])
outdata['lownormal'] = data['almanac']['temp_low']['normal']['F'] + "F" outdata['highnormal'] = self._temp(data['almanac']['temp_high']['normal']['F'])
if outdata['highyear'] != "NA" and outdata['lowyear'] != "NA": if outdata['highyear'] != "NA" and outdata['lowyear'] != "NA":
outdata['highrecord'] = data['almanac']['temp_high']['record']['F'] outdata['highrecord'] = self._temp(data['almanac']['temp_high']['record']['F'])
outdata['lowrecord'] = data['almanac']['temp_low']['record']['F'] outdata['lowrecord'] = self._temp(data['almanac']['temp_low']['record']['F'])
else: else:
outdata['highrecord'] = "NA" outdata['highrecord'] = outdata['lowrecord'] = "NA"
outdata['lowrecord'] = "NA"
else: # metric.
outdata['highnormal'] = data['almanac']['temp_high']['normal']['C'] + "C"
outdata['lownormal'] = data['almanac']['temp_low']['normal']['C'] + "C"
if outdata['highyear'] != "NA" and outdata['lowyear'] != "NA":
outdata['highrecord'] = data['almanac']['temp_high']['record']['C']
outdata['lowrecord'] = data['almanac']['temp_low']['record']['C']
else:
outdata['highrecord'] = "NA"
outdata['lowrecord'] = "NA"
# handle astronomy # handle astronomy
if args['astronomy']: if args['astronomy']:
@ -753,19 +597,7 @@ class Weather(callbacks.Plugin):
# now output to irc. # now output to irc.
irc.reply(output) irc.reply(output)
wunderground = wrap(wunderground, [getopts({'alerts':'', wunderground = wrap(wunderground, [optional('text')])
'almanac':'',
'astronomy':'',
'forecast':'',
'pressure':'',
'wind':'',
'uv':'',
'visibility':'',
'dewpoint':'',
'humidity':'',
'metric':'',
'nocolortemp':'',
'help':''}), optional('text')])
Class = Weather Class = Weather