You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
5.4 KiB
123 lines
5.4 KiB
from datetime import datetime
|
|
import pytz
|
|
from typing import List, Dict
|
|
from .models import LocationInfo, CurrentConditions, WeatherForecast, WeatherAlert
|
|
|
|
class Narrator:
|
|
STRINGS = {
|
|
'en': {
|
|
'wind_fmt': ", with winds from the {dir} at {kph} kilometers per hour, or {mph} miles per hour",
|
|
'current_intro': "Current conditions for {city}, {region}.",
|
|
'temp_fmt': "The temperature is {temp_c} degrees celsius, {temp_f} degrees fahrenheit.",
|
|
'conditions_fmt': "Conditions are {desc}{wind}.",
|
|
'forecast_intro': "Here is the forecast. ",
|
|
'high_fmt': " with a high of {c} celsius, {f} fahrenheit",
|
|
'low_fmt': " with a low of {c} celsius, {f} fahrenheit",
|
|
'period_fmt': "{period}: {summary}{temp}. ",
|
|
'no_alerts': "There are no active weather alerts.",
|
|
'alerts_intro': "There are {count} active weather alerts. ",
|
|
'alert_item': "A {title} is in effect until {expires}. ",
|
|
'greeting': "Good day. The time is {time}.",
|
|
'intro_city': "This is the automated weather report for {city}.",
|
|
'alert_advise': "Please be advised: ",
|
|
'time_fmt': "%I %M %p"
|
|
},
|
|
'fr': {
|
|
'wind_fmt': ", avec des vents du {dir} à {kph} kilomètres à l'heure",
|
|
'current_intro': "Conditions actuelles pour {city}, {region}.",
|
|
'temp_fmt': "La température est de {temp_c} degrés Celsius, {temp_f} degrés Fahrenheit.",
|
|
'conditions_fmt': "Les conditions sont {desc}{wind}.",
|
|
'forecast_intro': "Voici les prévisions. ",
|
|
'high_fmt': " avec un maximum de {c} Celsius, {f} Fahrenheit",
|
|
'low_fmt': " avec un minimum de {c} Celsius, {f} Fahrenheit",
|
|
'period_fmt': "{period}: {summary}{temp}. ",
|
|
'no_alerts': "Il n'y a aucune alerte météo en vigueur.",
|
|
'alerts_intro': "Il y a {count} alertes météo en vigueur. ",
|
|
'alert_item': "Un {title} est en vigueur jusqu'à {expires}. ",
|
|
'greeting': "Bonjour. Il est {time}.",
|
|
'intro_city': "Ceci est le bulletin météo automatisé pour {city}.",
|
|
'alert_advise': "Veuillez noter : ",
|
|
'time_fmt': "%H heures %M"
|
|
}
|
|
}
|
|
|
|
def __init__(self, config: Dict):
|
|
self.lang = config.get('language', 'en')
|
|
self.s = self.STRINGS.get(self.lang, self.STRINGS['en'])
|
|
|
|
def _c_to_f(self, temp_c: float) -> int:
|
|
return int((temp_c * 9/5) + 32)
|
|
|
|
def _t(self, key, **kwargs):
|
|
tpl = self.s.get(key, "")
|
|
return tpl.format(**kwargs)
|
|
|
|
def announce_conditions(self, loc: LocationInfo, current: CurrentConditions) -> str:
|
|
wind = ""
|
|
if current.wind_speed and current.wind_speed > 5:
|
|
mph = int(current.wind_speed * 0.621371)
|
|
wind = self._t('wind_fmt', dir=current.wind_direction, kph=int(current.wind_speed), mph=mph)
|
|
|
|
intro = self._t('current_intro', city=loc.city, region=loc.region)
|
|
temp_str = self._t('temp_fmt', temp_c=int(current.temperature), temp_f=self._c_to_f(current.temperature))
|
|
cond_str = self._t('conditions_fmt', desc=current.description, wind=wind)
|
|
|
|
return f"{intro} {temp_str} {cond_str}"
|
|
|
|
def announce_forecast(self, forecasts: List[WeatherForecast]) -> str:
|
|
if not forecasts:
|
|
return ""
|
|
|
|
text = self._t('forecast_intro')
|
|
for f in forecasts[:3]:
|
|
temp = ""
|
|
if f.high_temp is not None:
|
|
temp = self._t('high_fmt', c=int(f.high_temp), f=self._c_to_f(f.high_temp))
|
|
elif f.low_temp is not None:
|
|
temp = self._t('low_fmt', c=int(f.low_temp), f=self._c_to_f(f.low_temp))
|
|
|
|
text += self._t('period_fmt', period=f.period_name, summary=f.summary, temp=temp)
|
|
|
|
return text
|
|
|
|
def announce_alerts(self, alerts: List[WeatherAlert]) -> str:
|
|
if not alerts:
|
|
return self._t('no_alerts')
|
|
|
|
text = self._t('alerts_intro', count=len(alerts))
|
|
for a in alerts:
|
|
# Need locale specific time format?
|
|
# Alerts usually have specific expiration.
|
|
expires_str = a.expires.strftime(self.s.get('time_fmt', "%I %M %p"))
|
|
text += self._t('alert_item', title=a.title, expires=expires_str)
|
|
|
|
return text
|
|
|
|
def build_full_report(self, loc: LocationInfo, current: CurrentConditions, forecast: List[WeatherForecast], alerts: List[WeatherAlert], sun_info: str = "") -> str:
|
|
parts = []
|
|
|
|
try:
|
|
tz = pytz.timezone(loc.timezone)
|
|
now = datetime.now(tz)
|
|
except Exception:
|
|
now = datetime.now()
|
|
|
|
time_fmt = self.s.get('time_fmt', "%I %M %p")
|
|
now_str = now.strftime(time_fmt)
|
|
if self.lang == 'en' and now_str.startswith("0"):
|
|
now_str = now_str[1:]
|
|
|
|
parts.append(self._t('greeting', time=now_str))
|
|
parts.append(self._t('intro_city', city=loc.city))
|
|
|
|
if alerts:
|
|
parts.append(self._t('alert_advise') + self.announce_alerts(alerts))
|
|
|
|
parts.append(self.announce_conditions(loc, current))
|
|
parts.append(self.announce_forecast(forecast))
|
|
|
|
if sun_info:
|
|
parts.append(sun_info)
|
|
|
|
return " ".join(parts)
|