X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=weather_renderer.py;h=fbb3ed8170f873da0a3223088b8c60462a858c1a;hb=ba913d3c5ec6fd5e229398ebfe9e073aaae7d73c;hp=fdd4fe125d3257a1303797b338d6981dcaca56fc;hpb=75b27cc68871343681f01e3f5b04cae84b1b7b2a;p=kiosk.git diff --git a/weather_renderer.py b/weather_renderer.py index fdd4fe1..fbb3ed8 100644 --- a/weather_renderer.py +++ b/weather_renderer.py @@ -1,38 +1,41 @@ +#!/usr/bin/env python3 + from datetime import datetime -import file_writer -import renderer import json import re -import secrets +from typing import Dict, List import urllib.request, urllib.error, urllib.parse + +import file_writer +import renderer +import secrets import random + class weather_renderer(renderer.debuggable_abstaining_renderer): """A renderer to fetch forecast from wunderground.""" - def __init__(self, - name_to_timeout_dict, - file_prefix): + def __init__(self, name_to_timeout_dict: Dict[str, int], file_prefix: str) -> None: super(weather_renderer, self).__init__(name_to_timeout_dict, False) self.file_prefix = file_prefix - def debug_prefix(self): - return "weather(%s)" % (self.file_prefix) + def debug_prefix(self) -> str: + return f"weather({self.file_prefix})" - def periodic_render(self, key): + def periodic_render(self, key: str) -> bool: return self.fetch_weather() - def describe_time(self, index): - if (index <= 1): + def describe_time(self, index: int) -> str: + if index <= 1: return "overnight" - elif (index <= 3): + elif index <= 3: return "morning" - elif (index <= 5): + elif index <= 5: return "afternoon" else: return "evening" - def describe_wind(self, mph): + def describe_wind(self, mph: float) -> str: if mph <= 0.3: return "calm" elif mph <= 5.0: @@ -44,26 +47,26 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): else: return "heavy" - def describe_magnitude(self, mm): - if (mm < 2): + def describe_magnitude(self, mm: float) -> str: + if mm < 2.0: return "light" - elif (mm < 10): + elif mm < 10.0: return "moderate" else: return "heavy" - def describe_precip(self, rain, snow): - if rain == 0 and snow == 0: + def describe_precip(self, rain: float, snow: float) -> str: + if rain == 0.0 and snow == 0.0: return "no precipitation" magnitude = rain + snow if rain > 0 and snow > 0: - return "a %s mix of rain and snow" % self.describe_magnitude(magnitude) + return f"a {self.describe_magnitude(magnitude)} mix of rain and snow" elif rain > 0: - return "%s rain" % self.describe_magnitude(magnitude) + return f"{self.describe_magnitude(magnitude)} rain" elif snow > 0: - return "%s snow" % self.describe_magnitude(magnitude) + return f"{self.describe_magnitude(magnitude)} snow" - def fix_caps(self, s): + def fix_caps(self, s: str) -> str: r = "" s = s.lower() for x in s.split("."): @@ -72,7 +75,9 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): r = r.replace(". .", ".") return r - def pick_icon(self, conditions, rain, snow): + def pick_icon( + self, conditions: List[str], rain: List[float], snow: List[float] + ) -> str: # rain snow clouds sun # fog.gif # hazy.gif @@ -95,42 +100,48 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): total_snow = 0 count = min(len(conditions), len(rain), len(snow)) for x in range(0, count): - seen_rain = rain[x] > 0; - seen_snow = snow[x] > 0; + seen_rain = rain[x] > 0 + seen_snow = snow[x] > 0 total_snow += snow[x] txt = conditions[x].lower() - if ("cloud" in txt): + if "cloud" in txt: cloud_count += 1 - if ("clear" in txt or "sun" in txt): + if "clear" in txt or "sun" in txt: clear_count += 1 - if (seen_rain and seen_snow): - if (total_snow < 10): + if seen_rain and seen_snow: + if total_snow < 10: return "sleet.gif" else: return "snow.gif" - if (seen_snow): - if (total_snow < 10): + if seen_snow: + if total_snow < 10: return "flurries.gif" else: return "snow.gif" - if (seen_rain): + if seen_rain: return "rain.gif" - if (cloud_count >= 6): + if cloud_count >= 6: return "mostlycloudy.gif" - elif (cloud_count >= 4): + elif cloud_count >= 4: return "partlycloudy.gif" - if (clear_count >= 7): + if clear_count >= 7: return "sunny.gif" - elif (clear_count >= 6): + elif clear_count >= 6: return "mostlysunny.gif" - elif (clear_count >= 4): + elif clear_count >= 4: return "partlysunny.gif" return "clear.gif" - def describe_weather(self, - high, low, - wind, conditions, rain, snow): + def describe_weather( + self, + high: float, + low: float, + wind: List[float], + conditions: List[str], + rain: List[float], + snow: List[float], + ) -> str: # High temp: 65 # Low temp: 44 # -onight------ -morning----- -afternoon-- -evening---- @@ -159,24 +170,24 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): elif txt == "Rain": txt = "rainy" - if (txt != lcondition): + if txt != lcondition: if txt != "Snow" and txt != "Rain": current += txt chunks += 1 lcondition = txt txt = self.describe_wind(wind[x]) - if (txt != lwind): - if (len(current) > 0): + if txt != lwind: + if len(current) > 0: current += " with " current += txt + " winds" lwind = txt chunks += 1 txt = self.describe_precip(rain[x], snow[x]) - if (txt != lprecip): - if (len(current) > 0): - if (chunks > 1): + if txt != lprecip: + if len(current) > 0: + if chunks > 1: current += " and " else: current += " with " @@ -184,28 +195,28 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): current += txt lprecip = txt - if (len(current)): - if (ltime != time): - if (random.randint(0, 3) == 0): - if (time != "overnight"): + if len(current): + if ltime != time: + if random.randint(0, 3) == 0: + if time != "overnight": descr += current + " in the " + time + ". " descr += current + " overnight. " else: - if (time != "overnight"): + if time != "overnight": descr += "In the " descr += time + ", " + current + ". " else: current = current.replace("cloudy", "clouds") descr += current + " developing. " ltime = time - if (ltime == "overnight" or ltime == "morning"): + if ltime == "overnight" or ltime == "morning": descr += "Conditions continuing the rest of the day. " descr = descr.replace("with breezy winds", "and breezy") descr = descr.replace("Clear developing", "Skies clearing") descr = self.fix_caps(descr) return descr - def fetch_weather(self): + def fetch_weather(self) -> None: if self.file_prefix == "stevens": text_location = "Stevens Pass, WA" param = "lat=47.74&lon=-121.08" @@ -216,8 +227,10 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): text_location = "Bellevue, WA" param = "id=5786882" - www = urllib.request.urlopen('http://api.openweathermap.org/data/2.5/forecast?%s&APPID=%s&units=imperial' % ( - param, secrets.openweather_key)) + www = urllib.request.urlopen( + "http://api.openweathermap.org/data/2.5/forecast?%s&APPID=%s&units=imperial" + % (param, secrets.openweather_key) + ) response = www.read() www.close() parsed_json = json.loads(response) @@ -239,152 +252,178 @@ class weather_renderer(renderer.debuggable_abstaining_renderer): # "dt_txt":"2017-01-30 18:00:00" # }, # {"dt":1485810000,.... - f = file_writer.file_writer('weather-%s_3_10800.html' % self.file_prefix) - f.write(""" -

Weather at %s:

+ with file_writer.file_writer("weather-%s_3_10800.html" % self.file_prefix) as f: + f.write( + f""" +

Weather at {text_location}:


- - """ % text_location) - count = parsed_json['cnt'] - - ts = {} - highs = {} - lows = {} - wind = {} - conditions = {} - rain = {} - snow = {} - for x in range(0, count): - data = parsed_json['list'][x] - dt = data['dt_txt'] # 2019-10-07 18:00:00 - date = dt.split(" ")[0] - time = dt.split(" ")[1] - wind[date] = [] - conditions[date] = [] - highs[date] = -99999 - lows[date] = +99999 - rain[date] = [] - snow[date] = [] - ts[date] = 0 - - for x in range(0, count): - data = parsed_json['list'][x] - dt = data['dt_txt'] # 2019-10-07 18:00:00 - date = dt.split(" ")[0] - time = dt.split(" ")[1] - _ = data['dt'] - if (_ > ts[date]): - ts[date] = _ - temp = data["main"]["temp"] - if (highs[date] < temp): - highs[date] = temp - if (temp < lows[date]): - lows[date] = temp - wind[date].append(data["wind"]["speed"]) - conditions[date].append(data["weather"][0]["main"]) - if "rain" in data and "3h" in data["rain"]: - rain[date].append(data["rain"]["3h"]) - else: - rain[date].append(0) - if "snow" in data and "3h" in data["snow"]: - snow[date].append(data["snow"]["3h"]) - else: - snow[date].append(0) - - # {u'clouds': {u'all': 0}, - # u'sys': {u'pod': u'd'}, - # u'dt_txt': u'2019-10-09 21:00:00', - # u'weather': [ - # {u'main': u'Clear', - # u'id': 800, - # u'icon': u'01d', - # u'description': u'clear sky'} - # ], - # u'dt': 1570654800, - # u'main': { - # u'temp_kf': 0, - # u'temp': 54.74, - # u'grnd_level': 1018.95, - # u'temp_max': 54.74, - # u'sea_level': 1026.46, - # u'humidity': 37, - # u'pressure': 1026.46, - # u'temp_min': 54.74 - # }, - # u'wind': {u'speed': 6.31, u'deg': 10.09}} - - # Next 5 half-days - #for x in xrange(0, 5): - # fcast = parsed_json['forecast']['txt_forecast']['forecastday'][x] - # text = fcast['fcttext'] - # text = re.subn(r' ([0-9]+)F', r' \1°F', text)[0] - # f.write('' % text) - #f.write('

%s

') - #f.close() - #return True - - #f.write("\n") - days_seen = {} - for date in sorted(highs.keys()): - today = datetime.fromtimestamp(ts[date]) - formatted_date = today.strftime('%a %e %b') - if (formatted_date in days_seen): - continue; - days_seen[formatted_date] = True - num_days = len(list(days_seen.keys())) - - days_seen = {} - for date in sorted(highs.keys()): - precip = 0.0 - for _ in rain[date]: - precip += _ - for _ in snow[date]: - precip += _ - - today = datetime.fromtimestamp(ts[date]) - formatted_date = today.strftime('%a %e %b') - if (formatted_date in days_seen): - continue; - days_seen[formatted_date] = True - f.write('\n') - f.write("
\n' % (100 / num_days)) - f.write('\n') - - # Date - f.write(' \n') - - # Icon - f.write(' \n' % - self.pick_icon(conditions[date], rain[date], snow[date])) - - # Low temp - color = "#000099" - if (lows[date] <= 32.5): - color = "#009999" - f.write(' \n' % ( - color, int(lows[date]))) - - # Total precip - precip *= 0.0393701 - if (precip > 0.025): - f.write(' \n' % precip) - else: - f.write(' \n') - - # High temp - color = "#800000" - if (highs[date] >= 80): - color = "#AA0000" - f.write(' \n' % ( - color, int(highs[date]))) - - # Text "description" - f.write('\n' % - self.describe_weather(highs[date], lows[date], wind[date], conditions[date], rain[date], snow[date])) - f.write('
' + formatted_date + '
%d°F  
%3.1f"
   %d°F
%s
\n
") + + """ + ) + count = parsed_json["cnt"] + + ts = {} + highs = {} + lows = {} + wind = {} + conditions = {} + rain = {} + snow = {} + for x in range(0, count): + data = parsed_json["list"][x] + dt = data["dt_txt"] # 2019-10-07 18:00:00 + date = dt.split(" ")[0] + time = dt.split(" ")[1] + wind[date] = [] + conditions[date] = [] + highs[date] = -99999 + lows[date] = +99999 + rain[date] = [] + snow[date] = [] + ts[date] = 0 + + for x in range(0, count): + data = parsed_json["list"][x] + dt = data["dt_txt"] # 2019-10-07 18:00:00 + date = dt.split(" ")[0] + time = dt.split(" ")[1] + _ = data["dt"] + if _ > ts[date]: + ts[date] = _ + temp = data["main"]["temp"] + if highs[date] < temp: + highs[date] = temp + if temp < lows[date]: + lows[date] = temp + wind[date].append(data["wind"]["speed"]) + conditions[date].append(data["weather"][0]["main"]) + if "rain" in data and "3h" in data["rain"]: + rain[date].append(data["rain"]["3h"]) + else: + rain[date].append(0) + if "snow" in data and "3h" in data["snow"]: + snow[date].append(data["snow"]["3h"]) + else: + snow[date].append(0) + + # {u'clouds': {u'all': 0}, + # u'sys': {u'pod': u'd'}, + # u'dt_txt': u'2019-10-09 21:00:00', + # u'weather': [ + # {u'main': u'Clear', + # u'id': 800, + # u'icon': u'01d', + # u'description': u'clear sky'} + # ], + # u'dt': 1570654800, + # u'main': { + # u'temp_kf': 0, + # u'temp': 54.74, + # u'grnd_level': 1018.95, + # u'temp_max': 54.74, + # u'sea_level': 1026.46, + # u'humidity': 37, + # u'pressure': 1026.46, + # u'temp_min': 54.74 + # }, + # u'wind': {u'speed': 6.31, u'deg': 10.09}} + + # Next 5 half-days + # for x in xrange(0, 5): + # fcast = parsed_json['forecast']['txt_forecast']['forecastday'][x] + # text = fcast['fcttext'] + # text = re.subn(r' ([0-9]+)F', r' \1°F', text)[0] + # f.write('' % text) + # f.write('

%s

') + # f.close() + # return True + + # f.write("\n") + days_seen = {} + for date in sorted(highs.keys()): + today = datetime.fromtimestamp(ts[date]) + formatted_date = today.strftime("%a %e %b") + if formatted_date in days_seen: + continue + days_seen[formatted_date] = True + num_days = len(list(days_seen.keys())) + + days_seen = {} + for date in sorted(highs.keys()): + precip = 0.0 + for _ in rain[date]: + precip += _ + for _ in snow[date]: + precip += _ + + today = datetime.fromtimestamp(ts[date]) + formatted_date = today.strftime("%a %e %b") + if formatted_date in days_seen: + continue + days_seen[formatted_date] = True + f.write( + '\n") + f.write("
\n' % (100 / num_days) + ) + f.write("\n") + + # Date + f.write( + " \n" + ) + + # Icon + f.write( + ' \n' + % self.pick_icon(conditions[date], rain[date], snow[date]) + ) + + # Low temp + color = "#000099" + if lows[date] <= 32.5: + color = "#009999" + f.write( + ' \n' + % (color, int(lows[date])) + ) + + # Total precip + precip *= 0.0393701 + if precip > 0.025: + f.write( + ' \n' + % precip + ) + else: + f.write(" \n") + + # High temp + color = "#800000" + if highs[date] >= 80: + color = "#AA0000" + f.write( + ' \n' + % (color, int(highs[date])) + ) + + # Text "description" + f.write( + '\n' + % self.describe_weather( + highs[date], + lows[date], + wind[date], + conditions[date], + rain[date], + snow[date], + ) + ) + f.write("
" + + formatted_date + + "
%d°F  
%3.1f"
   %d°F
%s
\n
") return True -#x = weather_renderer({"Stevens": 1000}, -# "stevens") -#x.periodic_render("Stevens") + +# x = weather_renderer({"Stevens": 1000}, "stevens") +# x.periodic_render("Stevens")