Fixup weather renderer.
authorScott Gasch <[email protected]>
Tue, 21 Feb 2023 01:29:10 +0000 (17:29 -0800)
committerScott Gasch <[email protected]>
Tue, 21 Feb 2023 01:29:10 +0000 (17:29 -0800)
file_writer.py
weather_renderer.py

index ffb15e6b2ec4ed44da9308896b0388d006f2dc10..97f3e4b56a80b8c2912b1378edec1a2735589845 100644 (file)
@@ -1,11 +1,15 @@
 #!/usr/bin/env python3
 
+import logging
 import os
 from uuid import uuid4
 
 import kiosk_constants
 
 
+logger = logging.getLogger(__file__)
+
+
 class file_writer:
     """Helper context to write a pages file."""
 
@@ -16,6 +20,7 @@ class file_writer:
         self.xforms = [file_writer.remove_tricky_unicode]
         self.xforms.extend(transformations)
         self.f = None
+        logger.info("Writing {self.temp_filename}...")
 
     @staticmethod
     def remove_tricky_unicode(x: str) -> str:
@@ -38,6 +43,7 @@ class file_writer:
 
     def __exit__(self, exc_type, exc_value, exc_traceback):
         self.close()
+        logger.info(f"Copying {self.temp_filename} to {self.full_filename}")
         cmd = f'/bin/mv -f {self.temp_filename} "{self.full_filename}"'
         os.system(cmd)
         print(cmd)
@@ -50,8 +56,8 @@ class file_writer:
 
 
 # Test
-#def toupper(x):
+# def toupper(x):
 #   return x.upper()
 
-#with file_writer("test", transformations=[toupper]) as fw:
+# with file_writer("test", transformations=[toupper]) as fw:
 #   fw.write(u"Another test!!")
index 23c5a201e6e44f254c39c112a2d6c59451231ba5..4285fdefd5b646eb24f65a6ac24aba0febd35164 100644 (file)
@@ -1,16 +1,20 @@
 #!/usr/bin/env python3
 
-from datetime import datetime
+import logging
 import json
-from typing import Dict, List
 import urllib.request
 import urllib.error
 import urllib.parse
+from datetime import datetime
+from collections import defaultdict
+from typing import Dict, List
 
 import file_writer
 import renderer
 import kiosk_secrets as secrets
 
+logger = logging.getLogger(__file__)
+
 
 class weather_renderer(renderer.abstaining_renderer):
     """A renderer to fetch forecast from wunderground."""
@@ -19,12 +23,8 @@ class weather_renderer(renderer.abstaining_renderer):
         super().__init__(name_to_timeout_dict)
         self.file_prefix = file_prefix
 
-    def periodic_render(self, key: str) -> bool:
-        return self.fetch_weather()
-
-    def pick_icon(
-        self, conditions: List[str], rain: List[float], snow: List[float]
-    ) -> str:
+    @staticmethod
+    def pick_icon(conditions: List[str], rain: List[float], snow: List[float]) -> str:
         #                     rain     snow    clouds    sun
         # fog.gif
         # hazy.gif
@@ -80,6 +80,9 @@ class weather_renderer(renderer.abstaining_renderer):
             return "partlysunny.gif"
         return "clear.gif"
 
+    def periodic_render(self, key: str) -> bool:
+        return self.fetch_weather()
+
     def fetch_weather(self) -> bool:
         if self.file_prefix == "stevens":
             text_location = "Stevens Pass, WA"
@@ -90,14 +93,17 @@ class weather_renderer(renderer.abstaining_renderer):
         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)
-        )
+        secret = secrets.openweather_key
+        url = f"http://api.openweathermap.org/data/2.5/forecast?{param}&APPID={secret}&units=imperial"
+        logger.info(f"GETting {url}")
+        www = urllib.request.urlopen(url)
         response = www.read()
         www.close()
+        if www.getcode() != 200:
+            logger.error("Bad response: {response}")
+            raise Exception(response)
         parsed_json = json.loads(response)
+        logger.info("URL read ok")
 
         # https://openweathermap.org/forecast5
         # {"cod":"200",
@@ -118,11 +124,14 @@ class weather_renderer(renderer.abstaining_renderer):
         #     {"dt":1485810000,....
 
         with file_writer.file_writer(f"weather-{self.file_prefix}_3_10800.html") as f:
-            f.write(f"""
+            f.write(
+                f"""
 <h1>Upcoming weather at {text_location}:</h1>
 <hr>
-<script src="/kiosk/Chart.js"></script>""")
-            f.write("""
+<script src="/kiosk/Chart.js"></script>"""
+            )
+            f.write(
+                """
 <script>
 function makePrecipChart(name, xValues, yValues) {
   const config = {
@@ -155,7 +164,7 @@ function makePrecipChart(name, xValues, yValues) {
           display: false,
           beginAtZero: true,
           min: 0.0,
-          max: 10.0,
+          max: 12.0,
           grid: {
             display: false,
           },
@@ -167,45 +176,39 @@ function makePrecipChart(name, xValues, yValues) {
 }
 </script>
 <center>
-""")
+"""
+            )
             count = parsed_json["cnt"]
 
             ts = {}
             highs = {}
             lows = {}
-            wind: Dict[str, List[float]] = {}
-            conditions: Dict[str, List[str]] = {}
-            rain: Dict[str, List[float]] = {}
-            snow: Dict[str, List[float]] = {}
-            precip: Dict[str, List[float]] = {}
-
-            for x in range(0, count):
-                data = parsed_json["list"][x]
-                dt = data["dt_txt"]  # 2019-10-07 18:00:00
-                (date, time) = dt.split(' ')
-                wind[date] = []
-                conditions[date] = []
-                highs[date] = None
-                lows[date] = None
-                rain[date] = []
-                snow[date] = []
-                precip[date] = []
-                ts[date] = 0
+            wind: Dict[str, List[float]] = defaultdict(list)
+            conditions: Dict[str, List[str]] = defaultdict(list)
+            rain: Dict[str, List[float]] = defaultdict(list)
+            snow: Dict[str, List[float]] = defaultdict(list)
+            precip: Dict[str, List[float]] = defaultdict(list)
 
             for x in range(0, count):
                 data = parsed_json["list"][x]
                 dt = data["dt_txt"]  # 2019-10-07 18:00:00
-                (date, time) = dt.split(' ')
+                (date, time) = dt.split(" ")
                 _ = data["dt"]
-                if _ > ts[date]:
+                if _ not in ts or _ > ts[date]:
                     ts[date] = _
                 temp = data["main"]["temp"]
-                if highs[date] is None or highs[date] < temp:
+
+                # High and low temp
+                if date not in highs or highs[date] < temp:
                     highs[date] = temp
-                if lows[date] is None or temp < lows[date]:
+                if date not in lows or lows[date] > temp:
                     lows[date] = temp
+
+                # Windspeed and conditions
                 wind[date].append(data["wind"]["speed"])
                 conditions[date].append(data["weather"][0]["main"])
+
+                # 3h precipitation (rain / snow)
                 if "rain" in data and "3h" in data["rain"]:
                     rain[date].append(data["rain"]["3h"])
                 else:
@@ -257,18 +260,20 @@ function makePrecipChart(name, xValues, yValues) {
                     if remaining >= 3:
                         width = "33%"
                     else:
-                        width = f'{100/remaining}%'
+                        width = f"{100/remaining}%"
 
-                precip[date] = []
-                aggregate_precip = 0.0
+                aggregate_daily_precip = 0.0
                 for r, s in zip(rain[date], snow[date]):
-                    aggregate = r + s
-                    aggregate_precip += aggregate
-                    precip[date].append(aggregate)
+                    hourly_aggregate = r + s
+                    aggregate_daily_precip += hourly_aggregate
+                    precip[date].append(hourly_aggregate)
+                logger.debug(
+                    f"Aggregate precip on {date} was {aggregate_daily_precip} cm"
+                )
                 if first_day:
                     while len(precip[date]) < 8:
                         precip[date].insert(0, 0)
-                        first_day = False
+                    first_day = False
 
                 day = datetime.fromtimestamp(ts[date])
                 formatted_date = day.strftime("%a %e %b")
@@ -280,51 +285,61 @@ function makePrecipChart(name, xValues, yValues) {
                 )
 
                 # Date
-                f.write(f'''
+                f.write(
+                    f"""
 <tr>
   <td colspan=3 height=50>
     <center>
       <font size=7><b>{formatted_date}</b></font>
     </center>
   </td>
-</tr>''')
+</tr>"""
+                )
 
                 # Conditions icon
-                icon = self.pick_icon(conditions[date], rain[date], snow[date])
-                f.write(f'''
+                icon = weather_renderer.pick_icon(
+                    conditions[date], rain[date], snow[date]
+                )
+                f.write(
+                    f"""
 <tr>
   <td colspan=3 height=100>
     <center>
       <img src="/kiosk/images/weather/{icon}" height=145>
     </center>
   </td>
-</tr>''')
+</tr>"""
+                )
 
                 # Low temp -- left
                 color = "#000099"
                 if lows[date] <= 32.5:
                     color = "#009999"
-                f.write(f'''
+                f.write(
+                    f"""
 <tr>
   <td width=33% align=left>
     <font color="{color}" size=6>
       <b>{int(lows[date])}&deg;F&nbsp;&nbsp;</b>
     </font>
   </td>
-''')
+"""
+                )
 
                 # Total aggregate_precip in inches
-                aggregate_precip *= 0.0393701
-                if aggregate_precip > 0.025:
-                    f.write(f'''
+                aggregate_daily_precip /= 2.54
+                if aggregate_daily_precip > 0.025:
+                    f.write(
+                        f"""
   <td width=33%>
     <center>
       <font style="background-color:#dfdfff; color:#003355" size=6>
-        <b>{aggregate_precip:3.1f}&#8221;</b>
+        <b>{aggregate_daily_precip:3.1f}&#8221;</b>
       </font>
     </center>
   </td>
-''')
+"""
+                    )
                 else:
                     f.write("      <td width=33%>&nbsp;</td>\n")
 
@@ -332,34 +347,40 @@ function makePrecipChart(name, xValues, yValues) {
                 color = "#800000"
                 if highs[date] >= 80:
                     color = "#AA0000"
-                f.write(f'''
+                f.write(
+                    f"""
   <td align=right>
     <font color="{color}" size=6>
       <b>&nbsp;&nbsp;{int(highs[date])}&deg;F</b>
     </font>
   </td>
-</tr>''')
+</tr>"""
+                )
 
                 # Precip graph
-                f.write(f'''
+                f.write(
+                    f"""
 <tr>
   <td colspan=3 style="vertical-align:top;">
     <canvas id="myChart{n}" style="width:100%;max-width:400px;height:180px;"></canvas>
   </td>
 </tr>
 <script>
-var yValues{n} = ''')
+var yValues{n} = """
+                )
                 f.write(precip[date].__repr__())
-                f.write(f''';
+                f.write(
+                    f""";
 var xValues{n} = [ 3, 6, 9, 12, 15, 18, 21, 24 ];
 makePrecipChart("myChart{n}", xValues{n}, yValues{n});
 </script>
 </table>
 </div>
-''')
+"""
+                )
             f.write("</div></center>")
         return True
 
 
-#x = weather_renderer({"Stevens": 1000}, "stevens")
-#x.periodic_render("Stevens")
+# x = weather_renderer({"Stevens": 1000}, "stevens")
+# x.periodic_render("Stevens")