2022-06-19 15:21:28 -07:00

217 lines
7.2 KiB
Python

###
# Copyright (c) 2011-2014, Valentin Lorentz
# Copyright (c) 2018-2022, James Lu <james@overdrivenetworks.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import string
from supybot import ircutils, log
try:
import pendulum
except ImportError:
pendulum = None
log.warning('NuWeather: pendulum is not installed; extended forecasts will not be formatted properly')
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('NuWeather')
except ImportError:
_ = lambda x: x
# Based off https://github.com/ProgVal/Supybot-plugins/blob/master/GitHub/plugin.py
def flatten_subdicts(dicts, flat=None):
"""Flattens a dict containing dicts or lists of dicts. Useful for string formatting."""
if flat is None:
# Instanciate the dictionnary when the function is run and now when it
# is declared; otherwise the same dictionnary instance will be kept and
# it will have side effects (memory exhaustion, ...)
flat = {}
if isinstance(dicts, list):
return flatten_subdicts(dict(enumerate(dicts)))
elif isinstance(dicts, dict):
for key, value in dicts.items():
if isinstance(value, dict):
value = dict(flatten_subdicts(value))
for subkey, subvalue in value.items():
flat['%s__%s' % (key, subkey)] = subvalue
elif isinstance(value, list):
for num, subvalue in enumerate(value):
if isinstance(subvalue, dict):
for subkey, subvalue in subvalue.items():
flat['%s__%s__%s' % (key, num, subkey)] = subvalue
else:
flat['%s__%s' % (key, num)] = subvalue
else:
flat[key] = value
return flat
else:
return dicts
def format_temp(displaymode, f=None, c=None):
"""
Colorizes temperatures and formats them to show either Fahrenheit, Celsius, or both.
"""
if f is None and c is None:
return _('N/A')
if f is None:
f = c * 9/5 + 32
elif c is None:
c = (f - 32) * 5/9
f = float(f)
if f < 10:
color = 'light blue'
elif f < 32:
color = 'teal'
elif f < 50:
color = 'blue'
elif f < 60:
color = 'light green'
elif f < 70:
color = 'green'
elif f < 80:
color = 'yellow'
elif f < 90:
color = 'orange'
else:
color = 'red'
# Show temp values to one decimal place
c = '%.1f' % c
f = '%.1f' % f
if displaymode == 'F/C':
s = '%sF/%sC' % (f, c)
elif displaymode == 'C/F':
s = '%sC/%sF' % (c, f)
elif displaymode == 'F':
s = '%sF' % f
elif displaymode == 'C':
s = '%sC' % c
else:
raise ValueError("Unknown display mode for temperature.")
return ircutils.mircColor(s, color)
def wind_direction(angle):
"""Returns wind direction (N, W, S, E, etc.) given an angle."""
# Adapted from https://stackoverflow.com/a/7490772
directions = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW')
if angle is None:
return directions[0] # dummy output
angle = int(angle)
idx = int((angle/(360/len(directions)))+.5)
return directions[idx % len(directions)]
def format_uv(uv):
"""Formats UV levels with IRC colouring"""
if uv is None:
return _('N/A')
# From https://en.wikipedia.org/wiki/Ultraviolet_index#Index_usage 2018-12-30
uv = float(uv)
if uv <= 2.9:
color, risk = 'green', 'Low'
elif uv <= 5.9:
color, risk = 'yellow', 'Moderate'
elif uv <= 7.9:
color, risk = 'orange', 'High'
elif uv <= 10.9:
color, risk = 'red', 'Very high'
else:
# Closest we have to violet
color, risk = 'pink', 'Extreme'
s = '%d (%s)' % (uv, risk)
return ircutils.mircColor(s, color)
def format_precip(mm=None, inches=None):
"""Formats precipitation to mm/in format"""
if mm is None and inches is None:
return _('N/A')
elif mm == 0 or inches == 0:
return '0' # Don't bother with 2 units if the value is 0
if mm is None:
mm = round(inches * 25.4, 1)
elif inches is None:
inches = round(mm / 25.4, 1)
return _('%smm/%sin') % (mm, inches)
def format_distance(displaymode, mi=None, km=None, speed=False):
"""Formats distance or speed values in miles and kilometers"""
if mi is None and km is None:
return _('N/A')
if mi == 0 or km == 0:
return '0' # Don't bother with multiple units if the value is 0
if mi is None:
mi = km / 1.609344
elif km is None:
km = mi * 1.609344
if speed:
m = f'{round(km / 3.6, 1)}m/s'
mi = f'{round(mi, 1)}mph'
km = f'{round(km, 1)}km/h'
else:
m = f'{round(km * 1000, 1)}m'
mi = f'{round(mi, 1)}mi'
km = f'{round(km, 1)}km'
return string.Template(displaymode).safe_substitute(
{'mi': mi, 'km': km, 'm': m}
)
def format_percentage(value):
"""
Formats percentage values given either as an int (value%) or float (0 <= value <= 1).
"""
if isinstance(value, float):
return '%.0f%%' % (value * 100)
elif isinstance(value, int):
return '%d%%' % value
else:
return 'N/A'
def get_dayname(ts, idx, *, tz=None, fallback=None):
"""
Returns the day name given a Unix timestamp, day index and (optionally) a timezone.
"""
if pendulum is not None:
p = pendulum.from_timestamp(ts, tz=tz)
return p.format('dddd')
else:
if fallback:
return fallback
# Fallback
if idx == 0:
return 'Today'
elif idx == 1:
return 'Tomorrow'
else:
return 'Day_%d' % idx