From 624a2c3939fbd37a98a0908bf7ffc33f8c147d19 Mon Sep 17 00:00:00 2001
From: David Trattnig <david.trattnig@o94.at>
Date: Thu, 29 Oct 2020 20:24:54 +0100
Subject: [PATCH] Improved model. #50

---
 src/scheduling/fallback.py |  17 +++---
 src/scheduling/models.py   | 122 ++++++++++++++++++-------------------
 2 files changed, 68 insertions(+), 71 deletions(-)

diff --git a/src/scheduling/fallback.py b/src/scheduling/fallback.py
index 8e007177..3ad56091 100644
--- a/src/scheduling/fallback.py
+++ b/src/scheduling/fallback.py
@@ -117,7 +117,7 @@ class FallbackManager:
         fallback_type = None
 
         if self.validate_playlist(timeslot, "playlist"):
-            planned_playlist = timeslot.get_playlist()
+            planned_playlist = timeslot.playlist
             fallback_type = FallbackType.NONE
         else:
             (fallback_type, planned_playlist) = self.get_fallback_playlist(timeslot)            
@@ -134,16 +134,16 @@ class FallbackManager:
             timeslot (Timeslot)
 
         Returns:
-            (Playlist)
+            (Playlist) 
         """        
         playlist = None
         fallback_type = FallbackType.STATION
 
         if self.validate_playlist(timeslot, "schedule_fallback"):
-            playlist = timeslot.schedule_fallback[0]
+            playlist = timeslot.schedule_fallback
             fallback_type = FallbackType.SCHEDULE
         elif self.validate_playlist(timeslot, "show_fallback"):
-            playlist = timeslot.show_fallback[0]
+            playlist = timeslot.show_fallback
             fallback_type = FallbackType.SHOW
 
         return (fallback_type, playlist)
@@ -168,18 +168,17 @@ class FallbackManager:
         """
         playlist = getattr(timeslot, playlist_type)
         if playlist \
-            and isinstance(playlist, list) \
-            and playlist[0].entries \
-            and len(playlist[0].entries) > 0:
+            and playlist.entries \
+            and len(playlist.entries) > 0:
 
             # Default playlist
             if playlist_type == "playlist":
                 return True
 
             # Fallback playlist
-            elif playlist[0].entries:
+            elif playlist.entries:
                 is_fs_only = True
-                for entry in playlist[0].entries:
+                for entry in playlist.entries:
                     if entry.get_content_type() not in ResourceClass.FILE.types:
                         self.logger.error(SU.red("Fallback playlist of type '%s' contains not only file-system entries! \
                             Skipping fallback level..." % playlist_type))
diff --git a/src/scheduling/models.py b/src/scheduling/models.py
index 7cd8373e..90ab2f58 100644
--- a/src/scheduling/models.py
+++ b/src/scheduling/models.py
@@ -24,9 +24,9 @@ import datetime
 
 import sqlalchemy as sa
 
-from sqlalchemy.ext.declarative     import declarative_base
+from sqlalchemy                     import BigInteger, Boolean, Column, DateTime, Integer, String, ForeignKey, ColumnDefault
 from sqlalchemy                     import orm
-from sqlalchemy                     import BigInteger, Boolean, Column, DateTime, Integer, String, ForeignKey
+from sqlalchemy.ext.declarative     import declarative_base
 from sqlalchemy.orm                 import relationship
 from sqlalchemy.ext.hybrid          import hybrid_property
 
@@ -103,18 +103,9 @@ class AuraDatabaseModel():
     def recreate_db(systemexit = False):
         """
         Re-creates the database for developments purposes.
-        """
-        manualtimeslot = Timeslot()
-        manualtimeslot.timeslot_id = 0
-        manualtimeslot.show_name = "Manual Show"
+        """        
         Base.metadata.drop_all()
         Base.metadata.create_all()
-
-        
-        # self.logger.debug("inserting manual scheduling possibility and fallback trackservice timeslot")
-        # DB.session.add(manualtimeslot)
-        # db.session.add(fallback_trackservice_timeslot)
-        # self.logger.debug("all created. commiting...")
         DB.session.commit()
 
         if systemexit:
@@ -123,20 +114,43 @@ class AuraDatabaseModel():
 
 
 #
-#   SCHEDULES & PLAYLISTS
+#   TIMESLOT
 #
 
 
 class Timeslot(DB.Model, AuraDatabaseModel):
     """
-    One specific Timeslot for a show on a timeslot.
-    Holding references to playlists and fallback-playlists.
+    One specific timeslot for a show.
     """
     __tablename__ = 'timeslot'
 
     # Primary keys
     id = Column(Integer, primary_key=True, autoincrement=True)
 
+    # Relationships
+    playlist = relationship("Playlist",
+                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
+                                Timeslot.playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
+                            uselist=False, back_populates="timeslot")
+    schedule_fallback = relationship("Playlist",
+                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
+                                Timeslot.schedule_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
+                            uselist=False, back_populates="timeslot")
+    show_fallback = relationship("Playlist",
+                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
+                                Timeslot.show_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
+                            uselist=False, back_populates="timeslot")
+    station_fallback = relationship("Playlist",
+                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
+                                Timeslot.station_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
+                            uselist=False, back_populates="timeslot")
+
+    playlist_id = Column(Integer)
+    schedule_fallback_id = Column(Integer)
+    show_fallback_id = Column(Integer)
+    station_fallback_id = Column(Integer)
+
+    # Data
     timeslot_start = Column(DateTime, unique=True, index=True)
     timeslot_end = Column(DateTime, unique=True, index=True)
     timeslot_id = Column(Integer, unique=True)
@@ -151,29 +165,10 @@ class Timeslot(DB.Model, AuraDatabaseModel):
     category = Column(String(256))
     topic = Column(String(256))
     musicfocus = Column(String(256))
-
     is_repetition = Column(Boolean())
-
-    playlist_id = Column(Integer) #, ForeignKey("playlist.playlist_id"))
-    schedule_fallback_id = Column(Integer)
-    show_fallback_id = Column(Integer)
-    station_fallback_id = Column(Integer)
     
     fadeouttimer = None # Used to fade-out the timeslot, even when entries are longer
 
-    playlist = relationship("Playlist",
-                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, Timeslot.playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
-                            back_populates="timeslot")
-    schedule_fallback = relationship("Playlist",
-                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, Timeslot.schedule_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
-                            back_populates="timeslot")
-    show_fallback = relationship("Playlist",
-                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, Timeslot.show_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
-                            back_populates="timeslot")
-    station_fallback = relationship("Playlist",
-                            primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, Timeslot.station_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
-                            back_populates="timeslot")
-
 
     @staticmethod
     def select_show_on_datetime(date_time):
@@ -213,16 +208,6 @@ class Timeslot(DB.Model, AuraDatabaseModel):
         return timeslots
 
 
-    def get_playlist(self):
-        """
-        Returns the assigned playlist.
-        """
-        # TODO Refactor to avoid storing array of playlists.
-        if self.playlist and self.playlist[0]:
-            return self.playlist[0]
-        return None
-
-
     def has_queued_entries(self):
         """
         Checks if entries of this timeslot have been queued at the engine.        
@@ -291,6 +276,9 @@ class Timeslot(DB.Model, AuraDatabaseModel):
         return "ID#%s [Show: %s, ShowID: %s | %s - %s ]" % (str(self.timeslot_id), self.show_name, str(self.show_id), time_start, time_end)
 
 
+#
+#   PLAYLIST
+#
 
 class Playlist(DB.Model, AuraDatabaseModel):
     """
@@ -298,14 +286,16 @@ class Playlist(DB.Model, AuraDatabaseModel):
     """
     __tablename__ = 'playlist'
 
-    # pk,fk
+    # Primary and Foreign Key
     artificial_id = Column(Integer, primary_key=True)
     timeslot_start = Column(DateTime, ForeignKey("timeslot.timeslot_start"))
-    # relationships
+
+    # Relationships
     timeslot = relationship("Timeslot", uselist=False, back_populates="playlist")
     entries = relationship("PlaylistEntry", back_populates="playlist")
-    # data
-    playlist_id = Column(Integer, autoincrement=False)  # , ForeignKey("timeslot.playlist_id"))
+
+    # Data
+    playlist_id = Column(Integer, autoincrement=False)
     show_name = Column(String(256))
     entry_count = Column(Integer)
 
@@ -434,6 +424,9 @@ class Playlist(DB.Model, AuraDatabaseModel):
         return "ID#%s [items: %s | %s - %s]" % (str(self.playlist_id), str(self.entry_count), str(time_start), str(time_end))
 
 
+#
+#   PLAYLIST ENTRY
+#
 
 class PlaylistEntry(DB.Model, AuraDatabaseModel):
     """
@@ -441,29 +434,30 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel):
     """
     __tablename__ = 'playlist_entry'
 
-    # primary keys
+    # Primary and Foreign Keys
     artificial_id = Column(Integer, primary_key=True)
-
-    # foreign keys
     artificial_playlist_id = Column(Integer, ForeignKey("playlist.artificial_id"))
-    entry_num = Column(Integer) # , primary_key=True)
 
+    # Relationships
+    playlist = relationship("Playlist", uselist=False, back_populates="entries")
+    meta_data = relationship("PlaylistEntryMetaData", uselist=False, back_populates="entry")
+
+    # Data
+    entry_num = Column(Integer)
     uri = Column(String(1024))
     duration = Column(BigInteger)
+    volume = Column(Integer, ColumnDefault(100))
     source = Column(String(1024))
     entry_start = Column(DateTime)
+
     entry_start_actual = None # Assigned when the entry is actually played
     channel = None # Assigned when entry is actually played
     queue_state = None # Assigned when entry is about to be queued    
     status = None # Assigned when state changes
-    
     switchtimer = None
     loadtimer = None
     fadeouttimer = None
 
-    # relationships
-    playlist = relationship("Playlist", uselist=False, back_populates="entries")
-    meta_data = relationship("PlaylistEntryMetaData", uselist=False, back_populates="entry")
 
     @staticmethod
     def select_playlistentry_for_playlist(artificial_playlist_id, entry_num):
@@ -503,9 +497,6 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel):
     def end_unix(self):
         return time.mktime(self.entry_end.timetuple())
 
-    @hybrid_property
-    def volume(self):
-        return 100 # FIXME Make DB Column
 
     def get_content_type(self):
         return ResourceUtil.get_content_type(self.uri)
@@ -572,22 +563,29 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel):
 
 
 
+#
+#   PLAYLIST ENTRY METADATA
+#
+
 
 class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel):
     """
-    Metadata for a playlist entry such as the artist and track name.
+    Metadata for a playlist entry such as the artist, album and track name.
     """
     __tablename__ = "playlist_entry_metadata"
 
+    # Primary and Foreign Keys
     artificial_id = Column(Integer, primary_key=True)
     artificial_entry_id = Column(Integer, ForeignKey("playlist_entry.artificial_id"))
 
+    # Relationships
+    entry = relationship("PlaylistEntry", uselist=False, back_populates="meta_data")
+
+    # Data
     artist = Column(String(256))
     title = Column(String(256))
     album = Column(String(256))
 
-    entry = relationship("PlaylistEntry", uselist=False, back_populates="meta_data")
-
     @staticmethod
     def select_metadata_for_entry(artificial_playlistentry_id):
         return DB.session.query(PlaylistEntryMetaData).filter(PlaylistEntryMetaData.artificial_entry_id == artificial_playlistentry_id).first()
-- 
GitLab