diff --git a/libraries/database/broadcasts.py b/libraries/database/broadcasts.py index 03f47cab52ec3b3023a3dc14429f41519c0f3a69..8c02f160b55ec69ce9f13f0962237aef9d1fc0c3 100644 --- a/libraries/database/broadcasts.py +++ b/libraries/database/broadcasts.py @@ -189,15 +189,15 @@ class Schedule(DB.Model, AuraDatabaseModel): is_repetition = Column(Boolean()) playlist_id = Column(Integer) #, ForeignKey("playlist.playlist_id")) - timeslot_fallback_id = Column(Integer) + schedule_fallback_id = Column(Integer) show_fallback_id = Column(Integer) station_fallback_id = Column(Integer) playlist = relationship("Playlist", primaryjoin="and_(Schedule.schedule_start==Playlist.schedule_start, Schedule.playlist_id==Playlist.playlist_id, Schedule.show_name==Playlist.show_name)", back_populates="schedule") - timeslot_fallback = relationship("Playlist", - primaryjoin="and_(Schedule.schedule_start==Playlist.schedule_start, Schedule.timeslot_fallback_id==Playlist.playlist_id, Schedule.show_name==Playlist.show_name)", + schedule_fallback = relationship("Playlist", + primaryjoin="and_(Schedule.schedule_start==Playlist.schedule_start, Schedule.schedule_fallback_id==Playlist.playlist_id, Schedule.show_name==Playlist.show_name)", back_populates="schedule") show_fallback = relationship("Playlist", primaryjoin="and_(Schedule.schedule_start==Playlist.schedule_start, Schedule.show_fallback_id==Playlist.playlist_id, Schedule.show_name==Playlist.show_name)", @@ -289,12 +289,20 @@ class Playlist(DB.Model, AuraDatabaseModel): Raises: Exception: In case there a inconsistent database state, such es multiple playlists for given date/time. """ - playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == datetime and Playlist.playlist_id == playlist_id).all() - if playlists and len(playlists) > 1: - raise Exception("Inconsistent Database State: Multiple playlists for given schedule '%s' and playlist id#%d available!" % (str(datetime), playlist_id)) - if not playlists: - return None - return playlists[0] + playlist = None + playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == datetime).all() + # FIXME There are unknown issues with the native SQL query by ID + # playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == datetime and Playlist.playlist_id == playlist_id).all() + + for p in playlists: + if p.playlist_id == playlist_id: + playlist = p + # if playlists and len(playlists) > 1: + # raise Exception("Inconsistent Database State: Multiple playlists for given schedule '%s' and playlist id#%d available!" % (str(datetime), playlist_id)) + # if not playlists: + # return None + # return playlists[0] + return playlist @staticmethod diff --git a/modules/scheduling/calendar.py b/modules/scheduling/calendar.py index 98712e1b92032f142a1d2cd0e6155fcb28b7dcbb..f7153b99b602a1be7fe7cfece1fccaddff2a49ff 100644 --- a/modules/scheduling/calendar.py +++ b/modules/scheduling/calendar.py @@ -52,7 +52,8 @@ class AuraCalendarService(threading.Thread): _stop_event = None logger = None fetched_schedule_data = None - url = dict() + # FIXME is it needed? + #url = dict() data = dict() calendar_fetcher = None @@ -75,8 +76,9 @@ class AuraCalendarService(threading.Thread): self._stop_event = threading.Event() - self.__set_url__("calendar") - self.__set_url__("importer") + # FIXME is it needed? + # self.__set_url__("api_calendar_url") + # self.__set_url__("api_playlist_url") self.calendar_fetcher = CalendarFetcher(config) @@ -141,26 +143,28 @@ class AuraCalendarService(threading.Thread): self.logger.debug("Schedule data: " + str(fetched_schedule_data)) ret_schedule = [] - # for schedule in self.fetched_schedule_data: - # if "start" not in schedule: - # self.logger.warning("No start of schedule given. skipping the schedule: "+str(schedule)) - # continue - # if "end" not in schedule: - # self.logger.warning("No end of schedule given. skipping the schedule: "+str(schedule)) - # continue for schedule in fetched_schedule_data: + + # Check schedule for validity + if "start" not in schedule: + self.logger.warning("No 'start' of schedule given. Skipping the schedule: %s " % str(schedule)) + continue + if "end" not in schedule: + self.logger.warning("No 'end' of schedule given. Skipping the schedule: %s " % str(schedule)) + continue + # Store the schedule schedule_db = self.store_schedule(schedule) # Store playlists to play - self.logger.warning("--- Storing playlist only ---") self.store_playlist(schedule_db, schedule_db.playlist_id, schedule["playlist"]) - - #FIXME Store fallbacks in DB logic - # self.store_schedule_playlist(schedule_db, schedule, "schedule_fallback", 1) - # self.store_schedule_playlist(schedule_db, schedule, "show_fallback", 2) - # self.store_schedule_playlist(schedule_db, schedule, "station_fallback", 3) + if schedule_db.schedule_fallback_id: + self.store_playlist(schedule_db, schedule_db.schedule_fallback_id, schedule["schedule_fallback"], 1) + if schedule_db.show_fallback_id: + self.store_playlist(schedule_db, schedule_db.show_fallback_id, schedule["show_fallback"], 2) + if schedule_db.station_fallback_id: + self.store_playlist(schedule_db, schedule_db.station_fallback_id, schedule["station_fallback"], 3) ret_schedule.append(schedule_db) @@ -215,9 +219,9 @@ class AuraCalendarService(threading.Thread): schedule_db.topic = schedule["show_topics"] schedule_db.musicfocus = schedule["show_musicfocus"] - if schedule["playlist_id"] is None: - # FIXME Manually assigned playlist ID. - schedule["playlist_id"] = 1 + # if schedule["playlist_id"] is None: + # # FIXME Manually assigned playlist ID. + # schedule["playlist_id"] = 1 schedule_db.playlist_id = schedule["playlist_id"] schedule_db.schedule_fallback_id = schedule["schedule_fallback_id"] @@ -228,7 +232,8 @@ class AuraCalendarService(threading.Thread): return schedule_db - # ------------------------------------------------------------------------------------------ # + + def store_playlist(self, schedule_db, playlist_id, fetched_playlist, fallbackplaylist_type=0): """ Stores the Playlist to the database. @@ -245,15 +250,20 @@ class AuraCalendarService(threading.Thread): playlist_db.schedule_start = schedule_db.schedule_start playlist_db.show_name = schedule_db.show_name playlist_db.fallback_type = fallbackplaylist_type - playlist_db.entry_count = len(fetched_playlist["entries"]) + if "entries" in fetched_playlist: + playlist_db.entry_count = len(fetched_playlist["entries"]) + else: + playlist_db.entry_count = 0 playlist_db.store(havetoadd, commit=True) - self.store_playlist_entries(playlist_db, fetched_playlist) + if playlist_db.entry_count > 0: + self.store_playlist_entries(playlist_db, fetched_playlist) return playlist_db + def store_playlist_entries(self, playlist_db, fetched_playlist): """ Stores the playlist entries to the database. @@ -285,6 +295,8 @@ class AuraCalendarService(threading.Thread): entry_num = entry_num + 1 time_marker += duration + + def store_playlist_entry_metadata(self, playlistentry_db, metadata): """ Stores the meta-data for a PlaylistEntry. @@ -297,8 +309,8 @@ class AuraCalendarService(threading.Thread): playlistentrymetadata_db.artificial_entry_id = playlistentry_db.artificial_id if "artist" not in metadata: - self.logger.warning("Artist not found in metadata for track '%s'. Setting to 'N/a'" % playlistentry_db.filename) - playlistentrymetadata_db.artist = "N/a" + self.logger.warning("Artist not found in metadata for track '%s'. Setting to 'n/a'" % playlistentry_db.filename) + playlistentrymetadata_db.artist = "n/a" else: playlistentrymetadata_db.artist = metadata["artist"] playlistentrymetadata_db.title = metadata["title"] @@ -307,6 +319,7 @@ class AuraCalendarService(threading.Thread): playlistentrymetadata_db.store(havetoadd, commit=True) + # ------------------------------------------------------------------------------------------ # # FIXME Needed? @@ -380,15 +393,17 @@ class AuraCalendarService(threading.Thread): return audio_file.info.length # ------------------------------------------------------------------------------------------ # - def __set_url__(self, type): - url = self.config.get(type+"url") - pos = url.find("?") + # FIXME is it needed? - if pos > 0: - self.url[type] = url[0:pos] - self.data[type] = url[pos:] - else: - self.url[type] = url + # def __set_url__(self, type): + # #url = self.config.get(type+"url") + # pos = url.find("?") + + # if pos > 0: + # self.url[type] = url[0:pos] + # self.data[type] = url[pos:] + # else: + # self.url[type] = url # ------------------------------------------------------------------------------------------ # def stop(self): diff --git a/modules/scheduling/calender_fetcher.py b/modules/scheduling/calender_fetcher.py index 238cbfc9c976dae9fc50b4fd3e88001a2b9eeaee..7bd3e3c7d533cc3f4aeca19248706386f05556c6 100644 --- a/modules/scheduling/calender_fetcher.py +++ b/modules/scheduling/calender_fetcher.py @@ -8,63 +8,86 @@ from datetime import datetime, timedelta #from modules.models.schedule import Schedule from modules.base.simpleutil import SimpleUtil + class CalendarFetcher: + """ + Fetches the schedules, playlists and playlist entries as JSON + via the API endpoints. + """ url = dict() url_parameter = dict() config = None logging = None has_already_fetched = False fetched_schedule_data = None + # FIXME another crutch because of the missing TANK used_random_playlist_ids = list() + def __init__(self, config): + """ + Constructor + + Args: + config (AuraConfig): Holds the engine configuration + """ self.config = config self.logger = logging.getLogger("AuraEngine") - self.__set_url__("calendar") - self.__set_url__("importer") - self.__set_url__("api_show_") + self.__set_url__("api_calendar_url") + self.__set_url__("api_playlist_url") + self.__set_url__("api_show_url") + + + # + # PUBLIC METHODS + # + def fetch(self): - # fetch upcoming schedules from STEERING + """ + Retrieve all required data from the API. + """ + + # Fetch upcoming schedules from STEERING try: self.logger.debug("Fetching schedules from STEERING") self.fetched_schedule_data = self.__fetch_schedule_data__() except urllib.error.HTTPError as e: - self.logger.critical("Cannot fetch from " + self.url["calendar"] + "! Reason: " + str(e)) + self.logger.critical("Cannot fetch from " + self.url["api_calendar_url"] + "! Reason: " + str(e)) self.fetched_schedule_data = None return None except (urllib.error.URLError, IOError, ValueError) as e: - self.logger.critical("Cannot connect to " + self.url["calendar"] + "! Reason: " + str(e)) + self.logger.critical("Cannot connect to " + self.url["api_calendar_url"] + "! Reason: " + str(e)) self.fetched_schedule_data = None return None - # fetch playlist and fallbacks to the schedules from TANK + # Fetch playlist and fallbacks to the schedules from TANK try: self.logger.debug("Fetching playlists from TANK") self.__fetch_schedule_playlists__() except urllib.error.HTTPError as e: - self.logger.critical("Cannot fetch from " + self.url["importer"] + "! Reason: " + str(e)) + self.logger.critical("Cannot fetch from " + self.url["api_playlist_url"] + "! Reason: " + str(e)) self.fetched_schedule_data = None return None except (urllib.error.URLError, IOError, ValueError) as e: - self.logger.critical("Cannot connect to " + self.url["importer"] + "! Reason: " + str(e)) + self.logger.critical("Cannot connect to " + self.url["api_playlist_url"] + "! Reason: " + str(e)) self.fetched_schedule_data = None return None return_data = [] - # gather returndata + # Gather returndata try: for schedule in self.fetched_schedule_data: - # skip schedule if no start or end is given + # 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)) + self.logger.warning("No start of schedule given. Skipping schedule: " + str(schedule)) schedule = None if "end" not in schedule: - self.logger.warning("No end of schedule given. skipping schedule: " + str(schedule)) + self.logger.warning("No end of schedule given. Skipping schedule: " + str(schedule)) schedule = None if "playlist" not in schedule: - self.logger.warning("No playlist for schedule given. skipping schedule: " + str(schedule)) + self.logger.warning("No playlist for schedule given. Skipping schedule: " + str(schedule)) schedule = None if schedule: @@ -76,9 +99,19 @@ class CalendarFetcher: return return_data - # ------------------------------------------------------------------------------------------ # + + + # + # PRIVATE METHODS + # + + + # FIXME Refactor for more transparent API requests. def __set_url__(self, type): - url = self.config.get(type+"url") + """ + Initializes URLs and parameters for API calls. + """ + url = self.config.get(type) pos = url.find("?") if pos > 0: @@ -87,30 +120,28 @@ class CalendarFetcher: else: self.url[type] = url - # ------------------------------------------------------------------------------------------ # + + def __fetch_schedule_data__(self): - servicetype = "calendar" + """ + Fetches schedule data from Steering. + + Returns: + ([Schedule]): An array of schedules + """ + servicetype = "api_calendar_url" schedule = None # fetch data from steering - html_response = self.__fetch_data__(servicetype) + url = self.__build_url__(servicetype) + html_response = self.__fetch_data__(servicetype, url) - # FIXME move hardcoded test-data to separate testing logic. # use testdata if response fails or is empty if not html_response or html_response == b"[]": self.logger.critical("Got no response from Steering!") - #html_response = '[{"schedule_id":1,"start":"' + (datetime.now() + timedelta(hours=0)).strftime('%Y-%m-%d %H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + '","show_id":9,"show_name":"FROzine","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":2,"schedule_fallback_id":12,"show_fallback_id":92,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"},{"schedule_id":2,"schedule_start":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + '","schedule_end":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00') + '","show_id":10,"show_name":"FROMat","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":4,"schedule_fallback_id":22,"show_fallback_id":102,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"},{"schedule_id":3,"schedule_start":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00') + '","schedule_end":"' + (datetime.now() + timedelta(hours=3)).strftime('%Y-%m-%d %H:00:00') + '","show_id":11,"show_name":"Radio für Senioren","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":6,"schedule_fallback_id":32,"show_fallback_id":112,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"}]' - - # use testdata if wanted - if self.config.get("use_test_data"): - # FIXME move hardcoded test-data to separate testing logic. - html_response = '[{"id":1,"schedule_id":1,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=0)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":9,"show_name":"TestData: FROzine","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":2,"schedule_fallback_id":12,"show_fallback_id":92,"station_fallback_id":1,"rtr_category":"string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"},' \ - '{"id":2,"schedule_id":2,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":10,"show_name":"TestData: FROMat","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":4,"schedule_fallback_id":22,"show_fallback_id":102,"station_fallback_id":1,"rtr_category":"TestData: string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"},' \ - '{"id":3,"schedule_id":3,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=3)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":11,"show_name":"TestData: Radio für Senioren","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":6,"schedule_fallback_id":32,"show_fallback_id":112,"station_fallback_id":1,"rtr_category":"TestData: string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"}]' - self.logger.critical("Using hardcoded Response!") - else: - html_response = "{}" + # FIXME move hardcoded test-data to separate testing logic. + html_response = self.get_test_schedules() # convert to dict schedule = simplejson.loads(html_response) @@ -123,59 +154,89 @@ class CalendarFetcher: #self.fetched_schedule_data = self.remove_unnecessary_data(schedule) return self.remove_unnecessary_data(schedule) - # ------------------------------------------------------------------------------------------ # + + def __fetch_schedule_playlists__(self): + """ + Fetches all playlists including fallback playlists for every schedule. + This method used the class member `fetched_schedule_data`` to iterate + over and extend schedule data. + """ # store fetched entries => do not have to fetch playlist_id more than once fetched_entries=[] try: - self.logger.warning("only fetching normal playlists. no fallbacks") for schedule in self.fetched_schedule_data: - # Enhance schedule with details of show (e.g. slug) + # Extend schedule with details of show (e.g. slug) schedule = self.__fetch_show_details__(schedule) - # retrieve playlist and the fallbacks for every schedule - # if a playlist (like station_fallback) is already fetched, it is not fetched again but reused - schedule["playlist"] = self.__fetch_schedule_playlist__(schedule, "playlist_id", fetched_entries) - #schedule["schedule_fallback"] = self.__fetch_schedule_playlist__(schedule, "schedule_fallback_id", fetched_entries) - #schedule["show_fallback"] = self.__fetch_schedule_playlist__(schedule, "show_fallback_id", fetched_entries) - #schedule["station_fallback"] = self.__fetch_schedule_playlist__(schedule, "station_fallback_id", fetched_entries) - #self.logger.info(str(schedule)) + # Retrieve playlist and the fallback playlists for every schedule. + # If a playlist (like station_fallback) is already fetched, it is not fetched again but reused + schedule["playlist"] = self.__fetch_schedule_playlist__(schedule, "playlist_id", fetched_entries) + schedule["schedule_fallback"] = self.__fetch_schedule_playlist__(schedule, "schedule_fallback_id", fetched_entries) + schedule["show_fallback"] = self.__fetch_schedule_playlist__(schedule, "show_fallback_id", fetched_entries) + schedule["station_fallback"] = self.__fetch_schedule_playlist__(schedule, "station_fallback_id", fetched_entries) except Exception as e: self.logger.error("Error: "+str(e)) - # ------------------------------------------------------------------------------------------ # + + def __fetch_show_details__(self, schedule): - servicetype = "api_show_" + """ + Fetches details of a show from Steering. - json_response = self.__fetch_data__(servicetype, "${ID}", str(schedule["show_id"])) + Args: + schedule (Schedule): A schedule holding a valid `show_id` + + Returns: + (Schedule): The given schedule with additional show fields set. + """ + servicetype = "api_show_url" + + url = self.__build_url__(servicetype, "${ID}", str(schedule["show_id"])) + json_response = self.__fetch_data__(servicetype, url) show_details = simplejson.loads(json_response) - # Augment "schedules" with details of "show" + # Extend "schedules" with details of "show" schedule["show_slug"] = show_details["slug"] - ### ... add more properties here, if needed ... ### + ### ... + ### ... Add more properties here, if needed + ### ... return schedule - # ------------------------------------------------------------------------------------------ # - def __fetch_schedule_playlist__(self, schedule, id_name, fetched_schedule_entries): - servicetype = "importer" + + + def __fetch_schedule_playlist__(self, schedule, id_name, fetched_playlists): + """ + Fetches the playlist for a given schedule. + + Args: + schedule (Schedule): The schedule to fetch playlists for + id_name (String): The type of playlist to fetch (e.g. normal vs. fallback) + fetched_playlists ([]): Previously fetched playlists to avoid re-fetching + + Returns: + ([Schedule]): Array of playlists + """ + servicetype = "api_playlist_url" # fetch playlists from TANK if not "show_slug" in schedule: raise ValueError("Missing 'show_slug' for schedule", schedule) slug = str(schedule["show_slug"]) - json_response = self.__fetch_data__(servicetype, "${SLUG}", slug) + url = self.__build_url__(servicetype, "${SLUG}", slug) + json_response = self.__fetch_data__(servicetype, url) # if a playlist is already fetched, do not fetch it again - for entry in fetched_schedule_entries: - # FIXME schedule["playlist_id"] is always None, review if entry["id"] is valid - if entry["id"] == schedule[id_name]: - self.logger.debug("playlist #" + str(schedule[id_name]) + " already fetched") - return entry + for playlist in fetched_playlists: + # FIXME schedule["playlist_id"] is always None, review if playlist["id"] is valid + if playlist["id"] == schedule[id_name]: + self.logger.debug("Playlist #" + str(schedule[id_name]) + " already fetched") + return playlist if self.config.get("use_test_data"): # FIXME move hardcoded test-data to separate testing logic. @@ -183,30 +244,76 @@ class CalendarFetcher: json_response = self.create_test_data(id_name, schedule) # convert to list - schedule_entries = simplejson.loads(json_response) - if "results" in schedule_entries: - schedule_entries = schedule_entries["results"][0] - - for entry in schedule_entries["entries"]: - if entry["uri"].startswith("file"): - entry["filename"] = self.convert_to_filename(entry["uri"]) + playlists = simplejson.loads(json_response) + pl = None + + if "results" in playlists: + # FIXME Currently we use hardcoded playlist and fallback assignments due to issues in Dashboard/Steering/Tank + self.logger.warn("FIXME Currently we use hardcoded playlist and fallback assignments due to issues in Dashboard/Steering/Tank") + i = 0 + for playlist in playlists["results"]: + + pl = playlist + # FIXME Always use the first playlist, since the schedule.playlist_id is currently not set via Dashboard: + if i == 0 and id_name == "playlist_id": + schedule["playlist_id"] = playlist["id"] + break + # FIXME Currently it's not possible to set & query the fallback for a timeslot/show/station; therefore hardcode it: + elif i == 1 and id_name == "schedule_fallback_id": + schedule["schedule_fallback_id"] = playlist["id"] + break + elif i == 2 and id_name == "show_fallback_id": + schedule["show_fallback_id"] = playlist["id"] + break + elif i == 3 and id_name == "station_fallback_id": + schedule["station_fallback_id"] = playlist["id"] + break + else: + pl = None + i += 1 + + if pl: + # Note: playlists without entries are allowed -> will trigger fallbacks + if "entries" in pl: + for entry in pl["entries"]: + if entry["uri"].startswith("file"): + entry["filename"] = self.convert_to_filename(entry["uri"]) + + fetched_playlists.append(pl) + + + return pl - fetched_schedule_entries.append(schedule_entries) - return schedule_entries def convert_to_filename(self, uri): - # convert to normal filename + """ + Converts a file-system URI to an actual, absolute path to the file. + + Args: + uri (String): The URI of the file + + Returns: + path (String): Absolute file path + """ e = self.config.get("audiofolder") + "/" + uri[7:] + ".flac" if not os.path.isfile(e): self.logger.warning("File %s does not exist!" % e) return e - # ------------------------------------------------------------------------------------------ # - def __fetch_data__(self, type, placeholder=None, value=None): - # Init html_response + + + def __fetch_data__(self, type, url): + """ + Fetches JSON data for the given URL. + + Args: + url (String): The API endpoint to call + + Returns: + (Byte[]): An UTF-8 encoded byte object holding the response + """ html_response = b'' - url = self.__build_url__(type, placeholder, value) # Send request to the API and read the data try: @@ -239,7 +346,7 @@ class CalendarFetcher: url = self.url[type] if placeholder: url = url.replace(placeholder, value) - # print("built URL: "+url) + # self.logger.info("built URL: "+url) return url @@ -304,7 +411,28 @@ class CalendarFetcher: return items - # ------------------------------------------------------------------------------------------ # + + + # + # TESTING + # + + + def get_test_schedules(self): + html_response = "{}" + + # use testdata if wanted + if self.config.get("use_test_data"): + + html_response = '[{"id":1,"schedule_id":1,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=0)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":9,"show_name":"TestData: FROzine","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":2,"schedule_fallback_id":12,"show_fallback_id":92,"station_fallback_id":1,"rtr_category":"string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"},' \ + '{"id":2,"schedule_id":2,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":10,"show_name":"TestData: FROMat","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":4,"schedule_fallback_id":22,"show_fallback_id":102,"station_fallback_id":1,"rtr_category":"TestData: string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"},' \ + '{"id":3,"schedule_id":3,"automation-id":1,"className":"TestData","memo":"TestData","show_fundingcategory":"TestData","start":"' + (datetime.now() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:00:00') + '","end":"' + (datetime.now() + timedelta(hours=3)).strftime('%Y-%m-%dT%H:00:00') + '","show_id":11,"show_name":"TestData: Radio für Senioren","show_hosts":"TestData: Sandra Hochholzer, Martina Schweiger","title":"TestData:title","is_repetition":false,"playlist_id":6,"schedule_fallback_id":32,"show_fallback_id":112,"station_fallback_id":1,"rtr_category":"TestData: string","comment":"TestData: Kommentar","show_languages":"TestData: Sprachen","show_type":"TestData: Typ","show_categories":"TestData: Kategorie","show_topics":"TestData: Topic","show_musicfocus":"TestData: Fokus"}]' + self.logger.critical("Using hardcoded Response!") + + return html_response + + + def create_test_data(self, id_name, schedule): import random rand_id = random.randint(1, 10000)