From 4562c3535ba3df3e78d00f4edc6dd7a072fcb399 Mon Sep 17 00:00:00 2001
From: David Trattnig <david.trattnig@o94.at>
Date: Fri, 26 Jun 2020 17:24:46 +0200
Subject: [PATCH] Active source logic.

---
 .../sample/sample-development.engine-api.ini  |   1 +
 src/models.py                                 |  31 ++++--
 src/service.py                                | 104 +++++++++++-------
 3 files changed, 86 insertions(+), 50 deletions(-)

diff --git a/config/sample/sample-development.engine-api.ini b/config/sample/sample-development.engine-api.ini
index 225e84f..7c706a5 100644
--- a/config/sample/sample-development.engine-api.ini
+++ b/config/sample/sample-development.engine-api.ini
@@ -46,6 +46,7 @@ sync_host="http://localhost:8010"
 ; host_id=0
 ; main_host_1="http://localhost:8008"
 ; main_host_2="http://localhost:8009"
+; default_source=1
 
 
 sync_api_store_playlog="/api/v1/playlog/store"
\ No newline at end of file
diff --git a/src/models.py b/src/models.py
index a55fbcc..9034b0c 100644
--- a/src/models.py
+++ b/src/models.py
@@ -46,8 +46,8 @@ class PlayLog(db.Model):
     track_type              = Column(Integer)
     timeslot_id             = Column(Integer)
     show_name               = Column(String(256))
-    log_source              = Column(Integer)
-    is_synced               = Column(Boolean)
+    log_source              = Column(Integer)       # The play-out source this log is coming from (e.g. engine1, engine2)
+    is_synced               = Column(Boolean)       # Only relevant for main nodes, in a multi-node setup
 
 
 
@@ -63,8 +63,8 @@ class PlayLog(db.Model):
         self.track_type      = data.track_type
         self.timeslot_id     = data.timeslot_id
         self.show_name       = data.show_name
-        self.log_source      = data.log_source      # The play-out source this log is coming from (e.g. engine1, engine2)
-        self.is_synced       = False                # Only relevant for main nodes, in a multi-node setup
+        self.log_source      = data.log_source      
+        self.is_synced       = False                
 
 
     def save(self):
@@ -178,7 +178,8 @@ class TrackSchema(ma.SQLAlchemySchema):
 
 class ActivityLog(db.Model):
     """
-    Table holding a log of play-out source active and sync states.
+    Table holding a log of play-out source activity and their sync states.
+
     Only used in "SYNC" deployment mode.
     """
     __tablename__ = 'activity_log'
@@ -193,20 +194,30 @@ class ActivityLog(db.Model):
 
     def __init__(self, source_number):
         """
-        Initializes a activity entry
+        Initializes an activity entry
         """
         self.log_time = datetime.datetime.now()
         self.source_number = source_number
         self.is_synced = False
 
 
+    def get_active_source():
+        """
+        Retrieves the currently active source.
+        """
+        db.session.commit()
+        source = db.session.query(ActivityLog).\
+            order_by(ActivityLog.log_time.desc()).first()
+        return source
+
+
     def save(self):
         db.session.add(self)
         db.session.commit()
 
 
 
-class HealthHistory(db.Model):
+class HealthHistoryEntry(db.Model):
     """
     Table holding an history of health information for sources.
     """
@@ -218,15 +229,19 @@ class HealthHistory(db.Model):
     # Columns
     source_number        = Column(Integer)
     is_healthy           = Column(Boolean)
+    is_synced            = Column(Boolean)          # Only relevant for main nodes, in a multi-node setup
     health_info          = Column(String(2048))
 
 
-    def __init__(self, source_number):
+    def __init__(self, source_number, is_healthy, health_info):
         """
         Initializes an health entry.
         """
         self.log_time = datetime.datetime.now()
         self.source_number = source_number
+        self.is_healthy = is_healthy
+        self.is_synced = False
+        self.health_info = health_info
 
 
     def save(self):
diff --git a/src/service.py b/src/service.py
index d2073fd..818eb8a 100644
--- a/src/service.py
+++ b/src/service.py
@@ -35,7 +35,7 @@ class NodeType(Enum):
 
 class ApiService():
     """
-    Service handling API actions.
+    Service handling for API actions.
     """
 
     config = None
@@ -43,7 +43,7 @@ class ApiService():
     node_type = None
     sync_host = None
     main_hosts = None
-
+    active_source = None
 
     def __init__(self, config, logger):
         """
@@ -64,7 +64,7 @@ class ApiService():
             self.main_hosts = [ config.get("main_host_1"), config.get("main_host_2") ]
             if not self.main_hosts[0] and not self.main_hosts[1]:
                 self.logger.warn("Not a single main host defined. Be aware what you are doing.")
-                msg = "No sync possible as no host nodes are configured."
+                msg = "No sync possible as no host nodes are configured"
             else:
                 msg = "Syncing data of hosts '%s'" % (self.main_hosts)
 
@@ -73,11 +73,21 @@ class ApiService():
             self.node_type = NodeType.MAIN
             self.sync_host = config.get("sync_host")
             if not self.sync_host:
-                msg = "No child node for synchronization defined."
+                msg = "No child node for synchronization defined"
             else:
                 msg = "Pushing data to '%s'" % (self.sync_host)
 
-        self.logger.info("Running in '%s' mode. %s" % (self.node_type, msg))
+        # Set active source
+        self.active_source = ActivityLog.get_active_source()
+        if not self.active_source:
+            if self.node_type == NodeType.MAIN:
+                source_number = self.config.get("host_id")
+            else:
+                source_number = self.config.get("default_source")
+            self.set_active_source(source_number)
+        self.logger("Active source: %s" % self.active_source)
+
+        self.logger.info("Running in '%s' mode. %s." % (self.node_type, msg))
 
 
 
@@ -137,75 +147,85 @@ class ApiService():
         if not data.log_source:
             data.log_source = self.host_id
 
-        playlog = PlayLog(data)
-        playlog.save()
-        self.logger.debug("Stored playlog for '%s'" % playlog.track_start)
-
-        # Main Node: Push to Sync Node, if enabled
-        if self.node_type = NodeType.MAIN and self.sync_host:
-            playlog_schema = PlayLogSchema()
-            json_playlog = playlog_schema.dump(playlog)
-
-            try:
-                api_url = self.sync_host + self.config.get("sync_api_store_playlog")
-                r = requests.post(api_url, json=json_playlog)
-                if r.status_code == 200:
-                    self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host))
-                    playlog.is_synced = True
-                    playlog.save()
-                else:
-                    self.logger.error("Error while pushing playlog to sync-node: " + r.json())
-            except Exception as e:
-                self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e)
+        # Main Node: Alway log entry, independed of the source
+        # Sync Node: Only log entry when it's coming from an active source
+        if self.node_type == NodeType.MAIN or \
+            (self.node_type == NodeType.SYNC and data.log_source == self.active_source):
+
+            playlog = PlayLog(data)
+            playlog.save()
+            self.logger.debug("Stored playlog for '%s'" % playlog.track_start)
+
+            # Main Node: Push to Sync Node, if enabled
+            if self.node_type == NodeType.MAIN and self.sync_host:
+                playlog_schema = PlayLogSchema()
+                json_playlog = playlog_schema.dump(playlog)
+
+                try:
+                    api_url = self.sync_host + self.config.get("sync_api_store_playlog")
+                    r = requests.post(api_url, json=json_playlog)
+                    if r.status_code == 200:
+                        self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host))
+                        playlog.is_synced = True
+                        playlog.save()
+                    else:
+                        self.logger.error("Error while pushing playlog to sync-node: " + r.json())
+                except Exception as e:
+                    self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e)
+        else:
+            self.logger.info("Ditching playlog sent from an inactive source")
 
         
     def clock_info(self):
         """
-        Retrieves the currently playing playlist.  
+        Retrieves the dataset required to render the studio clock.  
         """
-        return "current playlist"
+        return "studio clock data"
 
 
-    def activate_engine(self, engine_number):
+    def set_active_source(self, source_number):
         """
-        Activates one engine and deactivates the other
+        Sets the active source (engine1, engine2, other) identified by its source number.
 
         Args:
-            engine_number (Integer): Number of the engine
+            source_number (Integer): Number of the engine
         """
-        return 'do some magic!'
+        if self.active_source > 0:
+            self.active_source = source_number
+            activity_log = ActivityLog(active_source)
+            activity_log.save()
 
 
-    def get_active_engine(self):
+    def get_active_source(self):
         """
-        Retrieves the status entry of the currently active engine
+        Retrieves number of the currently active source (engine1, engine2, other)
 
         Returns:
-            StatusEntry
+            (Integer)
         """
-        return 'do some magic!'
+        return self.active_source
 
 
-    def get_engine_health(self, engine_number):
+    def get_source_health(self, source_number):
         """
-        Retrieves the most recent health info of the requested engine
+        Retrieves the most recent health info of the requested source
 
         Args:
-            engine_number (Integer): Number of the engine
+            source_number (Integer): Number of the play-out source
 
 
         Returns:
-            (HealthInfo)
+            (HealthHistoryEntry)
         """
         return 'do some magic!'
 
 
-    def log_engine_health(self, engine_number):
+    def log_source_health(self, source_number):
         """
-        Logs another health entry for the given engine
+        Logs another health entry for the given source
 
         Args:
-            engine_number (Integer): Number of the engine
+            source_number (Integer): Number of the play-out source
 
         """
         return 'do some magic!'
-- 
GitLab