Commit 522904b0 authored by David Trattnig's avatar David Trattnig
Browse files

Rename "schedule" to "timeslot". #50

parent ac6edaff
......@@ -77,7 +77,7 @@ api_engine_store_health="http://localhost:8008/api/v1/source/health/${ENGINE_NUM
# How often should the calendar be fetched in seconds. This determines the time of the last changes applied, before a specific show aired
fetching_frequency=30
# The scheduling window defines when the entries of each schedule are queued for play-out in an ideal scenario.
# The scheduling window defines when the entries of each timeslot are queued for play-out in an ideal scenario.
# The actual window (scheduling_window_start - scheduling_window_end) should be higher then the `fetching_frequency` to allow proper queuing.
# Otherwise the fetch might never hit the scheduling window, because the scheduling logic is attached to the fetching logic.
......@@ -88,7 +88,7 @@ fetching_frequency=30
# - Update/Delete/Assignment of playlists and entries: Those are accepted until the **end** of the the scheduling window; existing queued entries are updated
#
# After the end of the scheduling window the pre-loading phase starts.
# Note, the values for windows is defined as a offset minus the actual start of the schedule in seconds.
# Note, the values for windows is defined as a offset minus the actual start of the timeslot in seconds.
scheduling_window_start=120
scheduling_window_end=15
# How many seconds before the actual schedule time the entry should be pre-rolled. Note to provide enough timeout for
......
......@@ -77,7 +77,7 @@ api_engine_store_health="http://127.0.0.1:8008/api/v1/source/health/${ENGINE_NUM
# How often should the calendar be fetched in seconds. This determines the time of the last changes applied, before a specific show aired
fetching_frequency=300
# The scheduling window defines when the entries of each schedule are queued for play-out in an ideal scenario.
# The scheduling window defines when the entries of each timeslot are queued for play-out in an ideal scenario.
# The actual window (scheduling_window_start - scheduling_window_end) should be higher then the `fetching_frequency` to allow proper queuing.
# Otherwise the fetch might never hit the scheduling window, because the scheduling logic is attached to the fetching logic.
......@@ -88,7 +88,7 @@ fetching_frequency=300
# - Update/Delete/Assignment of playlists and entries: Those are accepted until the **end** of the the scheduling window; existing queued entries are updated
#
# After the end of the scheduling window the pre-loading phase starts.
# Note, the values for windows is defined as a offset minus the actual start of the schedule in seconds.
# Note, the values for windows is defined as a offset minus the actual start of the timeslot in seconds.
scheduling_window_start=600
scheduling_window_end=60
# How many seconds before the actual schedule time the entry should be pre-rolled. Note to provide enough timeout for
......
......@@ -77,7 +77,7 @@ api_engine_store_health="http://localhost:8008/api/v1/source/health/${ENGINE_NUM
# How often should the calendar be fetched in seconds. This determines the time of the last changes applied, before a specific show aired
fetching_frequency=300
# The scheduling window defines when the entries of each schedule are queued for play-out in an ideal scenario.
# The scheduling window defines when the entries of each timeslot are queued for play-out in an ideal scenario.
# The actual window (scheduling_window_start - scheduling_window_end) should be higher then the `fetching_frequency` to allow proper queuing.
# Otherwise the fetch might never hit the scheduling window, because the scheduling logic is attached to the fetching logic.
......@@ -88,7 +88,7 @@ fetching_frequency=300
# - Update/Delete/Assignment of playlists and entries: Those are accepted until the **end** of the the scheduling window; existing queued entries are updated
#
# After the end of the scheduling window the pre-loading phase starts.
# Note, the values for windows is defined as a offset minus the actual start of the schedule in seconds.
# Note, the values for windows is defined as a offset minus the actual start of the timeslot in seconds.
scheduling_window_start=600
scheduling_window_end=60
# How many seconds before the actual schedule time the entry should be pre-rolled. Note to provide enough timeout for
......
......@@ -29,7 +29,7 @@ For example:
- Steering, to get the main incredient of an play-out engine: schedules (or "timeslots" in Steering terms),
which hold the actual information on playlists and their entries.
- Dashboard, to have a neat interface, being able to programm the schedules
- Dashboard, to have a neat interface, being able to programm the timeslots
- Tank, to get the references to audio files and other audio sources. Plus the actual files.
If you need to test and develop against the Engine's API you'll also need to get the `engine-api` project running.
......@@ -63,7 +63,7 @@ When you start Engine the following is happening:
2. Python `run.py`: Start Liquidsoap.
3. Liquidsoap: When Liquidsoap finished its startup, it creates a socket file as configured in `socketdir` of `engine.ini`.
4. Python `src/core/liquidsoap/client.py`: Connects to that socket file.
5. Python `src/schedulung/scheduler.py`: Continously loads schedules from the API endpoints, stores them in the local database and starts the playout as per the schedules.
5. Python `src/schedulung/scheduler.py`: Continously loads timeslots from the API endpoints, stores them in the local database and starts the playout as per the timeslots.
## More infos for debugging
......
......@@ -51,7 +51,7 @@ To configure your Icecast connectivity check-out the `[stream]` section in your
Engine provide a scheduling functionality by polling external API endpoints frequently.
Scheduling is split into multiple phase. Below you see a timline with one schedule planned at a certain
Scheduling is split into multiple phase. Below you see a timline with one timeslot planned at a certain
point in time and the involved phase before:
```ascii
......@@ -72,29 +72,29 @@ point in time and the involved phase before:
It does not involve related playlists and their entries. Those can still be modified after the
scheduling window has started.
The start and the end of the window is defined by the start of the schedule minus
The start and the end of the window is defined by the start of the timeslot minus
a configured amount of seconds (see `scheduling_window_start` and `scheduling_window_end`
in `engine.ini`).
During the scheduling window, the external API Endpoints are pulled continiously, to
check for updated schedules and related playlists. Also, any changes to playlists and
check for updated timeslots and related playlists. Also, any changes to playlists and
its entries are respected within that window (see `fetching_frequency` in `engine.ini`).
> Important: It's vital that the the scheduling window is wider than the fetching frequency.
Otherwise one fetch might never hit a scheduling window, hence not being able to schedule stuff.
- **Queuing and Pre-Loading**: Before any playlist entries of the schedule can be turned into
- **Queuing and Pre-Loading**: Before any playlist entries of the timeslot can be turned into
sound, they need to be queued and pre-loaded. Ideally the pre-loading happens somewhat before
the scheduled play-out time to avoid any delays in timing. Set the maximum time reserved for
pre-loading in your configuration (compare `preload_offset`in `engine.ini`).
> Important: The offset should not exceed the time between the end of the scheduling-window and the
start of the actual schedule playout.
start of the actual timeslot playout.
- **Play-out**: Finally the actual play-out is happening. The faders of the virtual mixers are pushed
all the way up, as soon it's "time to play" for one of the pre-loaded entries.
Transitions between playlist entries with different types of sources (file, stream and analog
inputs) are performed automatically. At the end of each schedule the channel is faded-out,
inputs) are performed automatically. At the end of each timeslot the channel is faded-out,
no matter if the total length of the playlist entries would require a longer timeslot.
If for some reason the playout is corrupted, stopped or too silent to make any sense, then
......@@ -114,7 +114,7 @@ The available fallbacks are evaluated in following order:
It's important to note, in case that playlists containing anything different than
file entries, are ignored (i.e. live or stream content).
2. **Show Fallback**: If the schedule for some show has no playlist assigned, the
2. **Show Fallback**: If the timeslot for some show has no playlist assigned, the
playlist assigned as a *show fallback* is used instead. In the dashboard this can
be done as seen in the screenshot below.
......
......@@ -23,7 +23,7 @@
class NoProgrammeLoadedException(Exception):
pass
class NoActiveScheduleException(Exception):
class NoActiveTimeslotException(Exception):
pass
......
......@@ -292,7 +292,7 @@ class Player:
def play(self, entry, transition):
"""
Plays a new `Entry`. In case of a new schedule (or some intented, immediate transition),
Plays a new `Entry`. In case of a new timeslot (or some intented, immediate transition),
a clean channel is selected and transitions between old and new channel is performed.
This method expects that the entry is pre-loaded using `preload(..)` or `preload_group(self, entries)`
......
......@@ -260,15 +260,15 @@ class EngineEventDispatcher():
thread.start()
def on_schedule_change(self, schedule):
def on_timeslot_change(self, timeslot):
"""
Called when the playlist or entries of the current schedule have changed.
Called when the playlist or entries of the current timeslot have changed.
"""
def func(self, schedule):
self.logger.debug("on_schedule_change(..)")
self.call_event("on_schedule_change", schedule)
def func(self, timeslot):
self.logger.debug("on_timeslot_change(..)")
self.call_event("on_timeslot_change", timeslot)
thread = Thread(target = func, args = (self, schedule))
thread = Thread(target = func, args = (self, timeslot))
thread.start()
......
......@@ -96,9 +96,9 @@ class TrackServiceHandler():
data["track_num"] = entry.entry_num
data["track_type"] = content_class.numeric
data["playlist_id"] = entry.playlist.playlist_id
data["schedule_id"] = entry.playlist.schedule.schedule_id
data["show_id"] = entry.playlist.schedule.show_id
data["show_name"] = entry.playlist.schedule.show_name
data["timeslot_id"] = entry.playlist.timeslot.timeslot_id
data["show_id"] = entry.playlist.timeslot.show_id
data["show_name"] = entry.playlist.timeslot.show_name
data["log_source"] = self.config.get("api_engine_number")
self.store_trackservice(data)
......@@ -130,9 +130,9 @@ class TrackServiceHandler():
# This is a playlog according to the scheduled playlist (normal or fallback)
data["track_num"] = entry.entry_num
data["playlist_id"] = entry.playlist.playlist_id
data["schedule_id"] = entry.playlist.schedule.schedule_id
data["show_id"] = entry.playlist.schedule.show_id
data["show_name"] = entry.playlist.schedule.show_name
data["timeslot_id"] = entry.playlist.timeslot.timeslot_id
data["show_id"] = entry.playlist.timeslot.show_id
data["show_name"] = entry.playlist.timeslot.show_name
else:
# This is a fallback playlog which wasn't scheduled actually (e.g. station fallback)
(past, timeslot, next) = self.playlog.get_timeslots()
......@@ -193,10 +193,10 @@ class TrackServiceHandler():
data["current_playlist"]["entries"].append(entry)
if current_timeslot:
data["current_schedule"] = current_timeslot
data["current_timeslot"] = current_timeslot
if next_timeslot:
data["next_schedule"] = next_timeslot
data["next_timeslot"] = next_timeslot
data = SU.clean_dictionary(data)
......@@ -251,20 +251,20 @@ class Playlog:
"""
data = {}
self.assign_fallback_playlist(data, None)
data["schedule_id"] = -1
data["timeslot_id"] = -1
data["show_id"] = -1
data["show_name"] = ""
if self.previous_timeslot:
data["schedule_start"] = self.previous_timeslot.get("schedule_end")
data["timeslot_start"] = self.previous_timeslot.get("timeslot_end")
else:
data["schedule_start"] = datetime.now()
data["timeslot_start"] = datetime.now()
if next_timeslot:
data["schedule_end"] = next_timeslot.schedule_start
data["timeslot_end"] = next_timeslot.timeslot_start
else:
# Fake the end, because the timeslot is actually not existing
data["schedule_end"] = datetime.now() + timedelta(hours=1)
data["timeslot_end"] = datetime.now() + timedelta(hours=1)
self.current_timeslot = data
......@@ -281,7 +281,7 @@ class Playlog:
timeslot (Timeslot): The current timeslot
"""
if timeslot and self.previous_timeslot:
if self.previous_timeslot.get("schedule_start") == timeslot.schedule_start:
if self.previous_timeslot.get("timeslot_start") == timeslot.timeslot_start:
return # Avoid overwrite by multiple calls in a row
data = {}
......@@ -294,16 +294,16 @@ class Playlog:
# A valid timeslot from the scheduler is available
if timeslot:
self.assign_fallback_playlist(data, timeslot)
data["schedule_id"] = timeslot.schedule_id
data["schedule_start"] = timeslot.schedule_start
data["schedule_end"] = timeslot.schedule_end
data["timeslot_id"] = timeslot.timeslot_id
data["timeslot_start"] = timeslot.timeslot_start
data["timeslot_end"] = timeslot.timeslot_end
data["show_id"] = timeslot.show_id
data["show_name"] = timeslot.show_name
data = SU.clean_dictionary(data)
# Any previous (fake) timeslots should get the proper end now
if not self.previous_timeslot:
self.current_timeslot["schedule_end"] = timeslot.schedule_start
self.current_timeslot["timeslot_end"] = timeslot.timeslot_start
self.previous_timeslot = self.current_timeslot
self.current_timeslot = data
......@@ -315,9 +315,9 @@ class Playlog:
if next_timeslot:
ns = {}
self.assign_fallback_playlist(ns, next_timeslot)
ns["schedule_id"] = next_timeslot.schedule_id
ns["schedule_start"] = next_timeslot.schedule_start
ns["schedule_end"] = next_timeslot.schedule_end
ns["timeslot_id"] = next_timeslot.timeslot_id
ns["timeslot_start"] = next_timeslot.timeslot_start
ns["timeslot_end"] = next_timeslot.timeslot_end
ns["show_id"] = next_timeslot.show_id
ns["show_name"] = next_timeslot.show_name
ns["playlist_id"] = next_timeslot.playlist_id
......
......@@ -27,7 +27,7 @@ import logging
from datetime import datetime
from src.base.utils import SimpleUtil as SU
from src.scheduling.models import Schedule, Playlist, PlaylistEntry, PlaylistEntryMetaData
from src.scheduling.models import Timeslot, Playlist, PlaylistEntry, PlaylistEntryMetaData
from src.scheduling.calender_fetcher import CalendarFetcher
......@@ -48,7 +48,7 @@ class AuraCalendarService(threading.Thread):
queue = None
config = None
logger = None
fetched_schedule_data = None
fetched_timeslot_data = None
calendar_fetcher = None
stop_event = None
......@@ -84,80 +84,80 @@ class AuraCalendarService(threading.Thread):
deleted schedules.
Returns
Schedule ([]): An arrar of retrieved schedules passed via `self.queue`
Timeslot ([]): An arrar of retrieved timeslots passed via `self.queue`
"""
result = []
now_unix = SU.timestamp()
scheduling_window_start = self.config.get("scheduling_window_start")
try:
fetched_schedule_data = self.calendar_fetcher.fetch()
self.logger.debug("Schedule data fetched from API: " + str(fetched_schedule_data))
fetched_timeslot_data = self.calendar_fetcher.fetch()
self.logger.debug("Timeslot data fetched from API: " + str(fetched_timeslot_data))
# If nothing is fetched, return
if not fetched_schedule_data:
if not fetched_timeslot_data:
self.queue.put("fetching_aborted Nothing fetched")
return
# Check if existing schedules have been deleted
local_schedules = Schedule.select_programme(datetime.now())
for local_schedule in local_schedules:
# Check if existing timeslots have been deleted
local_timeslots = Timeslot.select_programme(datetime.now())
for local_timeslot in local_timeslots:
# Only allow deletion of schedules which are deleted before the start of the scheduling window
if local_schedule.start_unix > now_unix:
if (local_schedule.start_unix - scheduling_window_start) > now_unix:
# Only allow deletion of timeslots which are deleted before the start of the scheduling window
if local_timeslot.start_unix > now_unix:
if (local_timeslot.start_unix - scheduling_window_start) > now_unix:
# Filter the local schedule from the fetched ones
existing_schedule = list(filter(lambda new_schedule: \
new_schedule["schedule_id"] == local_schedule.schedule_id, fetched_schedule_data))
# Filter the local timeslot from the fetched ones
existing_timeslot = list(filter(lambda new_timeslot: \
new_timeslot["timeslot_id"] == local_timeslot.timeslot_id, fetched_timeslot_data))
if existing_schedule:
# self.logger.debug("Schedule #%s is still existing remotely!" % (local_schedule.schedule_id))
if existing_timeslot:
# self.logger.debug("Timeslot #%s is still existing remotely!" % (local_timeslot.timeslot_id))
pass
else:
self.logger.info("Schedule #%s has been deleted remotely, hence also delete it locally [%s]" % \
(local_schedule.schedule_id, str(local_schedule)))
local_schedule.delete(commit=True)
self.logger.info("Deleted local schedule #%s from database" % local_schedule.schedule_id)
self.logger.info("Timeslot #%s has been deleted remotely, hence also delete it locally [%s]" % \
(local_timeslot.timeslot_id, str(local_timeslot)))
local_timeslot.delete(commit=True)
self.logger.info("Deleted local timeslot #%s from database" % local_timeslot.timeslot_id)
else:
msg = "Schedule #%s has been deleted remotely. Since the scheduling window has already started, it won't be deleted locally." % \
local_schedule.schedule_id
msg = "Timeslot #%s has been deleted remotely. Since the scheduling window has already started, it won't be deleted locally." % \
local_timeslot.timeslot_id
self.logger.error(SU.red(msg))
# Process fetched schedules
for schedule in fetched_schedule_data:
# Process fetched timeslots
for timeslot in fetched_timeslot_data:
# Check schedule for validity
if "start" not in schedule:
self.logger.warning("No 'start' of schedule given. Skipping the schedule: %s " % str(schedule))
# Check timeslot for validity
if "start" not in timeslot:
self.logger.warning("No 'start' of timeslot given. Skipping the timeslot: %s " % str(timeslot))
continue
if "end" not in schedule:
self.logger.warning("No 'end' of schedule given. Skipping the schedule: %s " % str(schedule))
if "end" not in timeslot:
self.logger.warning("No 'end' of timeslot given. Skipping the timeslot: %s " % str(timeslot))
continue
# Store the schedule
schedule_db = self.store_schedule(schedule)
# Store the timeslot
timeslot_db = self.store_timeslot(timeslot)
# Store playlists to play
self.store_playlist(schedule_db, schedule_db.playlist_id, schedule["playlist"])
if schedule_db.schedule_fallback_id:
self.store_playlist(schedule_db, schedule_db.schedule_fallback_id, schedule["schedule_fallback"])
if schedule_db.show_fallback_id:
self.store_playlist(schedule_db, schedule_db.show_fallback_id, schedule["show_fallback"])
if schedule_db.station_fallback_id:
self.store_playlist(schedule_db, schedule_db.station_fallback_id, schedule["station_fallback"])
self.store_playlist(timeslot_db, timeslot_db.playlist_id, timeslot["playlist"])
if timeslot_db.timeslot_fallback_id:
self.store_playlist(timeslot_db, timeslot_db.timeslot_fallback_id, timeslot["timeslot_fallback"])
if timeslot_db.show_fallback_id:
self.store_playlist(timeslot_db, timeslot_db.show_fallback_id, timeslot["show_fallback"])
if timeslot_db.station_fallback_id:
self.store_playlist(timeslot_db, timeslot_db.station_fallback_id, timeslot["station_fallback"])
# self.store_playlist(schedule_db, schedule_db.playlist_id, schedule["playlist"], PlaylistType.DEFAULT.id)
# if schedule_db.schedule_fallback_id:
# self.store_playlist(schedule_db, schedule_db.schedule_fallback_id, schedule["schedule_fallback"], PlaylistType.TIMESLOT.id)
# if schedule_db.show_fallback_id:
# self.store_playlist(schedule_db, schedule_db.show_fallback_id, schedule["show_fallback"], PlaylistType.SHOW.id)
# if schedule_db.station_fallback_id:
# self.store_playlist(schedule_db, schedule_db.station_fallback_id, schedule["station_fallback"], PlaylistType.STATION.id)
# self.store_playlist(timeslot_db, timeslot_db.playlist_id, timeslot["playlist"], PlaylistType.DEFAULT.id)
# if timeslot_db.timeslot_fallback_id:
# self.store_playlist(timeslot_db, timeslot_db.timeslot_fallback_id, timeslot["timeslot_fallback"], PlaylistType.TIMESLOT.id)
# if timeslot_db.show_fallback_id:
# self.store_playlist(timeslot_db, timeslot_db.show_fallback_id, timeslot["show_fallback"], PlaylistType.SHOW.id)
# if timeslot_db.station_fallback_id:
# self.store_playlist(timeslot_db, timeslot_db.station_fallback_id, timeslot["station_fallback"], PlaylistType.STATION.id)
result.append(schedule_db)
result.append(timeslot_db)
# Release the mutex
self.queue.put(result)
......@@ -171,49 +171,49 @@ class AuraCalendarService(threading.Thread):
def store_schedule(self, schedule):
def store_timeslot(self, timeslot):
"""
Stores the given schedule to the database.
Stores the given timeslot to the database.
Args:
schedule (Schedule): The schedule
timeslot (Timeslot): The timeslot
"""
schedule_db = Schedule.select_show_on_datetime(schedule["start"])
timeslot_db = Timeslot.select_show_on_datetime(timeslot["start"])
havetoadd = False
if not schedule_db:
self.logger.debug("no schedule with given schedule id in database => create new")
schedule_db = Schedule()
if not timeslot_db:
self.logger.debug("no timeslot with given timeslot id in database => create new")
timeslot_db = Timeslot()
havetoadd = True
schedule_db.show_id = schedule["show_id"]
schedule_db.schedule_id = schedule["schedule_id"]
schedule_db.schedule_start = schedule["start"]
schedule_db.schedule_end = schedule["end"]
schedule_db.show_name = schedule["show_name"]
schedule_db.show_hosts = schedule["show_hosts"]
schedule_db.is_repetition = schedule["is_repetition"]
schedule_db.funding_category = schedule["show_fundingcategory"]
schedule_db.languages = schedule["show_languages"]
schedule_db.type = schedule["show_type"]
schedule_db.category = schedule["show_categories"]
schedule_db.topic = schedule["show_topics"]
schedule_db.musicfocus = schedule["show_musicfocus"]
timeslot_db.show_id = timeslot["show_id"]
timeslot_db.timeslot_id = timeslot["timeslot_id"]
timeslot_db.timeslot_start = timeslot["start"]
timeslot_db.timeslot_end = timeslot["end"]
timeslot_db.show_name = timeslot["show_name"]
timeslot_db.show_hosts = timeslot["show_hosts"]
timeslot_db.is_repetition = timeslot["is_repetition"]
timeslot_db.funding_category = timeslot["show_fundingcategory"]
timeslot_db.languages = timeslot["show_languages"]
timeslot_db.type = timeslot["show_type"]
timeslot_db.category = timeslot["show_categories"]
timeslot_db.topic = timeslot["show_topics"]
timeslot_db.musicfocus = timeslot["show_musicfocus"]
schedule_db.playlist_id = schedule["playlist_id"]
schedule_db.schedule_fallback_id = schedule["schedule_fallback_id"]
schedule_db.show_fallback_id = schedule["show_fallback_id"]
schedule_db.station_fallback_id = schedule["station_fallback_id"]
timeslot_db.playlist_id = timeslot["playlist_id"]
timeslot_db.timeslot_fallback_id = timeslot["timeslot_fallback_id"]
timeslot_db.show_fallback_id = timeslot["show_fallback_id"]
timeslot_db.station_fallback_id = timeslot["station_fallback_id"]
schedule_db.store(add=havetoadd, commit=True)
timeslot_db.store(add=havetoadd, commit=True)
return schedule_db
return timeslot_db
# def store_playlist(self, schedule_db, playlist_id, fetched_playlist, fallbackplaylist_type=0):
def store_playlist(self, schedule_db, playlist_id, fetched_playlist):
# def store_playlist(self, timeslot_db, playlist_id, fetched_playlist, fallbackplaylist_type=0):
def store_playlist(self, timeslot_db, playlist_id, fetched_playlist):
"""
Stores the Playlist to the database.
"""
......@@ -222,17 +222,17 @@ class AuraCalendarService(threading.Thread):
# self.logger.debug("Playlist type %s with ID '%s' is not available!" % (fallbackplaylist_type, playlist_id))
return
playlist_db = Playlist.select_playlist_for_schedule(schedule_db.schedule_start, playlist_id)
playlist_db = Playlist.select_playlist_for_timeslot(timeslot_db.timeslot_start, playlist_id)
havetoadd = False
if not playlist_db:
playlist_db = Playlist()
havetoadd = True
self.logger.debug("Storing playlist %d for schedule (%s)" % (playlist_id, str(schedule_db)))
self.logger.debug("Storing playlist %d for timeslot (%s)" % (playlist_id, str(timeslot_db)))
playlist_db.playlist_id = playlist_id
playlist_db.schedule_start = schedule_db.schedule_start
playlist_db.show_name = schedule_db.show_name
playlist_db.timeslot_start = timeslot_db.timeslot_start
playlist_db.show_name = timeslot_db.show_name
# playlist_db.fallback_type = fallbackplaylist_type
if "entries" in fetched_playlist:
playlist_db.entry_count = len(fetched_playlist["entries"])
......@@ -242,20 +242,20 @@ class AuraCalendarService(threading.Thread):
playlist_db.store(havetoadd, commit=True)
if playlist_db.entry_count > 0:
self.store_playlist_entries(schedule_db, playlist_db, fetched_playlist)
self.store_playlist_entries(timeslot_db, playlist_db, fetched_playlist)
return playlist_db
def store_playlist_entries(self, schedule_db, playlist_db, fetched_playlist):
def store_playlist_entries(self, timeslot_db, playlist_db, fetched_playlist):
"""
Stores the playlist entries to the database.
"""
entry_num = 0
time_marker = playlist_db.start_unix
self.expand_entry_duration(schedule_db, fetched_playlist)
self.expand_entry_duration(timeslot_db, fetched_playlist)
self.delete_orphaned_entries(playlist_db, fetched_playlist)
for entry in fetched_playlist["entries"]:
......@@ -305,13 +305,13 @@ class AuraCalendarService(threading.Thread):
entry_num += 1
def expand_entry_duration(self, schedule_db, fetched_playlist):
def expand_entry_duration(self, timeslot_db, fetched_playlist):
"""
If some playlist entry doesn't have a duration assigned, its duration is expanded to the
remaining duration of the playlist (= schedule duration minus playlist entries with duration).
remaining duration of the playlist (= timeslot duration minus playlist entries with duration).
If there's more than one entry without duration, such entries are removed from the playlist.
"""
total_seconds = (schedule_db.schedule_end - schedule_db.schedule_start).total_seconds()
total_seconds = (timeslot_db.timeslot_end - timeslot_db.timeslot_start).total_seconds()
total_duration = SU.seconds_to_nano(total_seconds)
actual_duration = 0
missing_duration = []
......
......@@ -30,13 +30,13 @@ from src.base.utils import SimpleUtil as SU
class CalendarFetcher:
"""
Fetches the schedules, playlists and playlist entries as JSON
Fetches the timeslots, playlists and playlist entries as JSON
via the API endpoints of Steering and Tank.
"""
config = None
logging = None
has_already_fetched = False
fetched_schedule_data = None
fetched_timeslot_data = None
# Config for API Endpoints
steering_calendar_url = None
......@@ -72,37 +72,37 @@ class CalendarFetcher:
"""
return_data = []
self.logger.debug("Fetching schedules from STEERING")
self.fetched_schedule_data = self.fetch_schedule_data()
if not self.fetched_schedule_data:
self.logger.critical(SU.red("No schedules fetched from API!"))
self.logger.debug("Fetching timeslots from STEERING")
self.fetched_timeslot_data = self.fetch_timeslot_data()
if not self.fetched_timeslot_data:
self.logger.critical(SU.red("No timeslots fetched from API!"))
return None
self.logger.debug("Fetching playlists from TANK")
self.fetch_playlists()
try:
for schedule in self.fetched_schedule_data:
# Skip schedule if no start or end is given
if "start" not in schedule:
self.logger.warning("No start of schedule given. Skipping schedule: " + str(schedule))