from datetime import datetime import file_writer import renderer import json import re import secrets import urllib.request, urllib.error, urllib.parse 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): 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 periodic_render(self, key): return self.fetch_weather() def describe_time(self, index): if (index <= 1): return "overnight" elif (index <= 3): return "morning" elif (index <= 5): return "afternoon" else: return "evening" def describe_wind(self, mph): if mph <= 0.3: return "calm" elif mph <= 5.0: return "light" elif mph < 15.0: return "breezy" elif mph <= 25.0: return "gusty" else: return "heavy" def describe_magnitude(self, mm): if (mm < 2): return "light" elif (mm < 10): return "moderate" else: return "heavy" def describe_precip(self, rain, snow): if rain == 0 and snow == 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) elif rain > 0: return "%s rain" % self.describe_magnitude(magnitude) elif snow > 0: return "%s snow" % self.describe_magnitude(magnitude) def fix_caps(self, s): r = "" s = s.lower() for x in s.split("."): x = x.strip() r += x.capitalize() + ". " r = r.replace(". .", ".") return r def pick_icon(self, conditions, rain, snow): # rain snow clouds sun # fog.gif # hazy.gif # clear.gif # mostlycloudy.gif F F 6+ X # partlycloudy.gif F F 4+ 4- # cloudy.gif # partlysunny.gif F F X 5+ # mostlysunny.gif F F X 6+ # rain.gif T F X X # sleet.gif T T X X # flurries.gif F T X X (<1") # snow.gif F T X X (else) # sunny.gif F F X 7+ # tstorms.gif seen_rain = False seen_snow = False cloud_count = 0 clear_count = 0 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; total_snow += snow[x] txt = conditions[x].lower() if ("cloud" in txt): cloud_count += 1 if ("clear" in txt or "sun" in txt): clear_count += 1 if (seen_rain and seen_snow): if (total_snow < 10): return "sleet.gif" else: return "snow.gif" if (seen_snow): if (total_snow < 10): return "flurries.gif" else: return "snow.gif" if (seen_rain): return "rain.gif" if (cloud_count >= 6): return "mostlycloudy.gif" elif (cloud_count >= 4): return "partlycloudy.gif" if (clear_count >= 7): return "sunny.gif" elif (clear_count >= 6): return "mostlysunny.gif" elif (clear_count >= 4): return "partlysunny.gif" return "clear.gif" def describe_weather(self, high, low, wind, conditions, rain, snow): # High temp: 65 # Low temp: 44 # -onight------ -morning----- -afternoon-- -evening---- # 12a-3a 3a-6a 6a-9a 9a-12p 12p-3p 3p-6p 6p-9p 9p-12p # Wind: [12.1 3.06 3.47 4.12 3.69 3.31 2.73 2.1] # Conditions: [Clouds Clouds Clouds Clouds Clouds Clouds Clear Clear] # Rain: [0.4 0.2 0 0 0 0 0 0] # Snow: [0 0 0 0 0 0 0 0] high = int(high) low = int(low) count = min(len(wind), len(conditions), len(rain), len(snow)) descr = "" lcondition = "" lwind = "" lprecip = "" ltime = "" for x in range(0, count): time = self.describe_time(x) current = "" chunks = 0 txt = conditions[x] if txt == "Clouds": txt = "cloudy" elif txt == "Rain": txt = "rainy" 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): 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): current += " and " else: current += " with " chunks += 1 current += txt lprecip = txt 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"): descr += "In the " descr += time + ", " + current + ". " else: current = current.replace("cloudy", "clouds") descr += current + " developing. " ltime = time 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): if self.file_prefix == "stevens": text_location = "Stevens Pass, WA" param = "lat=47.74&lon=-121.08" elif self.file_prefix == "telma": text_location = "Telma, WA" param = "lat=47.84&lon=-120.81" else: 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)) response = www.read() www.close() parsed_json = json.loads(response) # https://openweathermap.org/forecast5 # {"cod":"200", # "message":0.0036, # "cnt":40, # "list":[ # {"dt":1485799200, # "main":{"temp":261.45,"temp_min":259.086,"temp_max":261.45,"pressure":1023.48,"sea_level":1045.39,"grnd_level":1023.48,"humidity":79,"temp_kf":2.37}, # "weather":[ # {"id":800,"main":"Clear","description":"clear sky","icon":"02n"} # ], # "clouds":{"all":8}, # "wind":{"speed":4.77,"deg":232.505}, # "snow":{}, # "sys":{"pod":"n"}, # "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:


""" % 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
") return True #x = weather_renderer({"Stevens": 1000}, # "stevens") #x.periodic_render("Stevens")