mirror of
https://github.com/jlu5/SupyPlugins.git
synced 2025-04-26 04:51:08 -05:00
217 lines
7.2 KiB
Python
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
|
|
|
|
|