Update cabin camera URL.
[kiosk.git] / renderer.py
index 721b374529ea96f7804a8cd6077271b0bbbb6e8b..55f540c026f481d577dade1b7936750865fae42b 100644 (file)
@@ -1,77 +1,80 @@
+#!/usr/bin/env python3
+
+from abc import ABC, abstractmethod
+import logging
 import time
-from datetime import datetime
+from typing import Dict, Optional, Set
+
+from pyutils.decorator_utils import invocation_logged
+
+
+logger = logging.getLogger(__name__)
+
 
-class renderer(object):
+class renderer(ABC):
     """Base class for something that can render."""
+
+    @abstractmethod
     def render(self):
         pass
 
+    @abstractmethod
+    def get_name(self):
+        pass
+
+
 class abstaining_renderer(renderer):
     """A renderer that doesn't do it all the time."""
-    def __init__(self, name_to_timeout_dict):
-        self.name_to_timeout_dict = name_to_timeout_dict;
+
+    def __init__(self, name_to_timeout_dict: Dict[str, int]) -> None:
+        self.name_to_timeout_dict = name_to_timeout_dict
         self.last_runs = {}
         for key in name_to_timeout_dict:
-            self.last_runs[key] = 0
+            self.last_runs[key] = 0.0
 
-    def should_render(self, keys_to_skip):
+    def should_render(self, keys_to_skip: Set[str]) -> Optional[str]:
         now = time.time()
         for key in self.name_to_timeout_dict:
-            if key in keys_to_skip:
-                continue
-            if (now - self.last_runs[key]) > self.name_to_timeout_dict[key]:
+            if (
+                (now - self.last_runs[key]) > self.name_to_timeout_dict[key]
+            ) and key not in keys_to_skip:
                 return key
         return None
 
-    def render(self):
-        tries = {}
-        keys_to_skip = set()
+    @invocation_logged
+    def render(self) -> None:
+        tries_per_key: Dict[str, int] = {}
+        keys_to_skip: Set[str] = set()
         while True:
             key = self.should_render(keys_to_skip)
-            if key == None:
+            if key is None:
+                logger.info(f'Found nothing to do in "{self.get_name()}"; returning.')
                 break
-            if key in tries:
-                tries[key] += 1
+
+            if key in tries_per_key:
+                tries_per_key[key] += 1
             else:
-                tries[key] = 0
+                tries_per_key[key] = 0
+            op = f"{self.get_name()}.{key}"
 
-            if tries[key] > 5:
-                print('Too many retries for "%s", giving up for now' % key)
+            if tries_per_key[key] >= 3:
+                logger.warning(f'Too many failures in "{op}"; giving up.')
                 keys_to_skip.add(key)
             else:
-                msg = 'renderer: periodic render event for "%s"' % key
-                if (tries[key] > 1):
-                    msg = msg + " (try %d)" % tries[key]
-                print(msg)
-                if (self.periodic_render(key)):
+                msg = f'Executing "{op}"'
+                if tries_per_key[key] > 1:
+                    msg = msg + f" (retry #{tries_per_key[key]})"
+                logger.info(msg)
+                if self.periodic_render(key):
+                    logger.debug(f'"{op}" succeeded.')
                     self.last_runs[key] = time.time()
+                else:
+                    logger.warning(f'"{op}" failed; returned False.')
 
-    def periodic_render(self, key):
+    @invocation_logged
+    @abstractmethod
+    def periodic_render(self, key) -> bool:
         pass
 
-class debuggable_abstaining_renderer(abstaining_renderer):
-    def __init__(self, name_to_timeout_dict, debug):
-        super(debuggable_abstaining_renderer, self).__init__(name_to_timeout_dict);
-        self.debug = debug
-
-    def debug_prefix(self):
-        return "none"
-
-    def being_debugged(self):
-        return self.debug
-
-    def debug_print(self, template, *args):
-        try:
-            if self.being_debugged():
-                if args:
-                    msg = template.format(args)
-                else:
-                    msg = template
-
-                # current date and time
-                now = datetime.now()
-                timestamp = now.strftime("%d-%b-%Y (%H:%M:%S.%f)")
-                print "%s(%s): %s" % (self.debug_prefix(), timestamp, msg)
-        except Exception as e:
-            print "Exception in debug_print!"
-            print e
+    def get_name(self) -> str:
+        return self.__class__.__name__