def test_check_song_for_conflict(self): db.c.update("DELETE FROM r4_listeners") db.c.update("DELETE FROM r4_request_store") e = Election.create(1) self.assertEqual(False, e._check_song_for_conflict(self.song1)) u = User(2) u.authorize(sid=1, ip_address=None, api_key=None, bypass=True) self.assertEqual(1, u.put_in_request_line(1)) # TODO: Use proper request/user methods here instead of DB call db.c.update( "UPDATE r4_request_line SET line_top_song_id = %s, line_expiry_tune_in = %s WHERE user_id = %s", (self.song1.id, int(time.time()) + 9999, u.id)) db.c.update( "INSERT INTO r4_listeners (sid, user_id, listener_icecast_id) VALUES (1, %s, 1)", (u.id, )) db.c.update( "INSERT INTO r4_request_store (user_id, song_id, sid) VALUES (%s, %s, 1)", (u.id, self.song1.id)) request.update_cache(1) request.update_expire_times() cache.update_local_cache_for_sid(1) self.assertEqual(True, e._check_song_for_conflict(self.song1)) self.assertEqual(True, e._check_song_for_conflict(self.song5)) self.assertEqual(event.ElecSongTypes.conflict, self.song5.data['entry_type']) self.assertEqual(event.ElecSongTypes.request, self.song1.data['entry_type'])
def post_process(sid): try: db.c.start_transaction() start_time = time.time() playlist.prepare_cooldown_algorithm(sid) rainwave.playlist_objects.album.clear_updated_albums(sid) log.debug("post", "Playlist prepare time: %.6f" % (time.time() - start_time,)) start_time = time.time() current[sid].finish() log.debug("post", "Current finish time: %.6f" % (time.time() - start_time,)) start_time = time.time() last_song = current[sid].get_song() if last_song: db.c.update("INSERT INTO r4_song_history (sid, song_id) VALUES (%s, %s)", (sid, last_song.id)) log.debug("post", "Last song insertion time: %s" % (time.time() - start_time,)) start_time = time.time() history[sid].insert(0, current[sid]) while len(history[sid]) > 5: history[sid].pop() log.debug("post", "History management: %.6f" % (time.time() - start_time,)) start_time = time.time() current[sid] = upnext[sid].pop(0) current[sid].start_event() log.debug("advance", "Current management: %.6f" % (time.time() - start_time,)) start_time = time.time() playlist.warm_cooled_songs(sid) playlist.warm_cooled_albums(sid) log.debug("advance", "Cooldown warming: %.6f" % (time.time() - start_time,)) start_time = time.time() _add_listener_count_record(sid) _trim(sid) user.trim_listeners(sid) cache.update_user_rating_acl(sid, history[sid][0].get_song().id) user.unlock_listeners(sid) log.debug("advance", "User management and trimming: %.6f" % (time.time() - start_time,)) start_time = time.time() # reduce song blocks has to come first, otherwise it wll reduce blocks generated by _create_elections playlist.reduce_song_blocks(sid) # update_cache updates both the line and expiry times # this is expensive and not necessary to do more than once # DO THIS AFTER EVERYTHING ELSE, RIGHT BEFORE NEXT MANAGEMENT, OR PEOPLE'S REQUESTS SLIP THROUGH THE CRACKS request.update_cache(sid) # add to the event list / update start times for events manage_next(sid) log.debug("advance", "Request and upnext management: %.6f" % (time.time() - start_time,)) update_memcache(sid) sync_to_front.sync_frontend_all(sid) db.c.commit() except: db.c.rollback() raise
def test_check_song_for_conflict(self): db.c.update("DELETE FROM r4_listeners") db.c.update("DELETE FROM r4_request_store") e = Election.create(1) self.assertEqual(False, e._check_song_for_conflict(self.song1)) u = User(2) u.authorize(1, None, None, True) self.assertEqual(1, u.put_in_request_line(1)) # TODO: Use proper request/user methods here instead of DB call db.c.update("UPDATE r4_request_line SET line_top_song_id = %s, line_expiry_tune_in = %s WHERE user_id = %s", (self.song1.id, time.time()+9999, u.id)) db.c.update("INSERT INTO r4_listeners (sid, user_id, listener_icecast_id) VALUES (1, %s, 1)", (u.id,)) db.c.update("INSERT INTO r4_request_store (user_id, song_id, sid) VALUES (%s, %s, 1)", (u.id, self.song1.id)) request.update_cache(1) cache.update_local_cache_for_sid(1) self.assertEqual(True, e._check_song_for_conflict(self.song1)) self.assertEqual(False, e._check_song_for_conflict(self.song5)) self.assertEqual(event.ElecSongTypes.conflict, self.song5.data['entry_type']) self.assertEqual(event.ElecSongTypes.request, self.song1.data['entry_type'])
def _create_elections(sid): # Step, er, 0: Update the request cache first, so elections have the most recent data to work with # (the entire requests module depends on its caches) request.update_cache(sid) max_sched_id, max_elec_id, num_elections = _get_schedule_stats(sid) log.debug( "create_elec", "Max sched ID: %s // Max elec ID: %s // Num elections already existing: %s // Size of next: %s" % (max_sched_id, max_elec_id, num_elections, len(next[sid]))) # Step 2: Load up any elections that have been added while we've been idle and append them to the list unused_elec_id = db.c.fetch_list( "SELECT elec_id FROM r4_elections WHERE sid = %s AND elec_id > %s AND elec_used = FALSE AND elec_priority = FALSE ORDER BY elec_id", (sid, max_elec_id)) unused_elecs = [] for elec_id in unused_elec_id: unused_elecs.append(event.Election.load_by_id(elec_id)) # Step 3: Insert elections where there's time and adjust predicted start times as necessary, if num_elections < 2 then create them where necessary i = 1 running_time = current[sid].start_actual + current[sid].length() if len(next[sid]) > 0: next[sid][0].start = running_time while i < len(next[sid]): next_start = next[sid][i].start gap = next_start - running_time next_elec_i = None next_elec_length = playlist.get_average_song_length(sid) j = i while j < len(next[sid]): if next[sid][j].is_election: next_elec = j next_elec_length = next[sid][j].length() break if not next_elec_i and len(unused_elecs) > 0: next_elec_length = unused_elecs[0].length() # TODO: This algorithm DEFINITELY needs code/concept review # There are potential holes - it is not as comprehensive a scheduler as the previous # Rainwave scheduler, however it is vastly simplified. # One drawback is that you cannot schedule elections themselves to run at certain times. create_elecs = False # If the event we're looking at collides with the previous event, adjust this event to start later if gap <= 0: next[sid][i].start = running_time running_time += next[sid][i].length() # If we have no elections current in the next list and there's enough time to fit a song, stuff an election in # (this automatically takes into account unused elections, based on next_elec_length definition above) elif not next_elec_i and gap <= (next_elec_length * 1.4): next_elec = None # If we have an existing unused election, we can use that (as next_elec_length is already based on the first unused elec, this can happen) if len(unused_elecs) > 0: next_elec = unused_elecs.pop(0) # If not, create a new election timed to the gap (next_elec_length will be the average song length*1.4, so this will happen frequently) else: next_elec = _create_election(sid, running_time, gap) num_elections += 1 next_elec.start = running_time running_time += next_elec.length() next[sid].insert(i, next_elec) # If it's more accurate to squeeze a created election in here than adjust the next event, move the event # *OR* the next event is too far out and we have elections in hand elif next_elec_i and ((gap <= (next_elec_length / 2)) or (gap > (next_elec_length * 1.5))): next_elec = next[sid].pop(next_elec_i) next_elec.start = running_time running_time += next_elec.length() next[sid].insert(i, next_elec) # The next event is better off aligned else: next[sid][i].start = running_time running_time += next[sid][i].length() i += 1 needed_elecs = config.get_station(sid, "num_planned_elections") - num_elections log.debug( "create_elec", "Before: Needed elecs: %s // Unused elecs: %s // Current num elections: %s // Next size: %s" % (needed_elecs, len(unused_elecs), num_elections, len(next[sid]))) # Step 5: If we're at less than X elections available, create them (or use unused ones) and append them # No timing is required here, since we're simply outright appending to the end # (any elections appearing before a scheduled item would be handled by the block above) failures = 0 while needed_elecs > 0 and failures <= 2: next_elec = None if len(unused_elecs) > 0: next_elec = unused_elecs.pop(0) else: next_elec = _create_election(sid, running_time) next_elec_length = next_elec.length() if next_elec_length > 0: next_elec.start = running_time running_time += next_elec.length() next[sid].append(next_elec) num_elections += 1 needed_elecs -= 1 else: log.error("create_elec", "Election ID %s was faulty - zero length. Deleting.") next_elec.delete() failures += 1 if failures >= 2: log.error("create_elec", "Total failure when creating elections.") log.debug( "create_elec", "After: Unused elecs: %s // Current num elections: %s // Next size: %s" % (len(unused_elecs), num_elections, len(next[sid])))
def post_process(sid): try: db.c.start_transaction() start_time = time.time() playlist.prepare_cooldown_algorithm(sid) rainwave.playlist_objects.album.clear_updated_albums(sid) log.debug("post", "Playlist prepare time: %.6f" % (time.time() - start_time, )) start_time = time.time() current[sid].finish() log.debug("post", "Current finish time: %.6f" % (time.time() - start_time, )) start_time = time.time() last_song = current[sid].get_song() if last_song: db.c.update( "INSERT INTO r4_song_history (sid, song_id) VALUES (%s, %s)", (sid, last_song.id)) log.debug( "post", "Last song insertion time: %s" % (time.time() - start_time, )) start_time = time.time() history[sid].insert(0, current[sid]) while len(history[sid]) > 5: history[sid].pop() log.debug("post", "History management: %.6f" % (time.time() - start_time, )) start_time = time.time() current[sid] = upnext[sid].pop(0) current[sid].start_event() log.debug("advance", "Current management: %.6f" % (time.time() - start_time, )) start_time = time.time() playlist.warm_cooled_songs(sid) playlist.warm_cooled_albums(sid) log.debug("advance", "Cooldown warming: %.6f" % (time.time() - start_time, )) start_time = time.time() _add_listener_count_record(sid) _trim(sid) user.trim_listeners(sid) cache.update_user_rating_acl(sid, history[sid][0].get_song().id) user.unlock_listeners(sid) log.debug( "advance", "User management and trimming: %.6f" % (time.time() - start_time, )) start_time = time.time() # reduce song blocks has to come first, otherwise it wll reduce blocks generated by _create_elections playlist.reduce_song_blocks(sid) # update_cache updates both the line and expiry times # this is expensive and not necessary to do more than once # DO THIS AFTER EVERYTHING ELSE, RIGHT BEFORE NEXT MANAGEMENT, OR PEOPLE'S REQUESTS SLIP THROUGH THE CRACKS request.update_cache(sid) # add to the event list / update start times for events manage_next(sid) log.debug( "advance", "Request and upnext management: %.6f" % (time.time() - start_time, )) update_memcache(sid) sync_to_front.sync_frontend_all(sid) db.c.commit() except: db.c.rollback() raise
def _create_elections(sid): # Step, er, 0: Update the request cache first, so elections have the most recent data to work with # (the entire requests module depends on its caches) request.update_cache(sid) max_sched_id, max_elec_id, num_elections = _get_schedule_stats(sid) log.debug("create_elec", "Max sched ID: %s // Max elec ID: %s // Num elections already existing: %s // Size of next: %s" % (max_sched_id, max_elec_id, num_elections, len(next[sid]))) # Step 2: Load up any elections that have been added while we've been idle and append them to the list unused_elec_id = db.c.fetch_list("SELECT elec_id FROM r4_elections WHERE sid = %s AND elec_id > %s AND elec_used = FALSE AND elec_priority = FALSE ORDER BY elec_id", (sid, max_elec_id)) unused_elecs = [] for elec_id in unused_elec_id: unused_elecs.append(event.Election.load_by_id(elec_id)) # Step 3: Insert elections where there's time and adjust predicted start times as necessary, if num_elections < 2 then create them where necessary i = 1 running_time = current[sid].start_actual + current[sid].length() if len(next[sid]) > 0: next[sid][0].start = running_time while i < len(next[sid]): next_start = next[sid][i].start gap = next_start - running_time next_elec_i = None next_elec_length = playlist.get_average_song_length(sid) j = i while j < len(next[sid]): if next[sid][j].is_election: next_elec = j next_elec_length = next[sid][j].length() break if not next_elec_i and len(unused_elecs) > 0: next_elec_length = unused_elecs[0].length() # TODO: This algorithm DEFINITELY needs code/concept review # There are potential holes - it is not as comprehensive a scheduler as the previous # Rainwave scheduler, however it is vastly simplified. # One drawback is that you cannot schedule elections themselves to run at certain times. create_elecs = False # If the event we're looking at collides with the previous event, adjust this event to start later if gap <= 0: next[sid][i].start = running_time running_time += next[sid][i].length() # If we have no elections current in the next list and there's enough time to fit a song, stuff an election in # (this automatically takes into account unused elections, based on next_elec_length definition above) elif not next_elec_i and gap <= (next_elec_length * 1.4): next_elec = None # If we have an existing unused election, we can use that (as next_elec_length is already based on the first unused elec, this can happen) if len(unused_elecs) > 0: next_elec = unused_elecs.pop(0) # If not, create a new election timed to the gap (next_elec_length will be the average song length*1.4, so this will happen frequently) else: next_elec = _create_election(sid, running_time, gap) num_elections += 1 next_elec.start = running_time running_time += next_elec.length() next[sid].insert(i, next_elec) # If it's more accurate to squeeze a created election in here than adjust the next event, move the event # *OR* the next event is too far out and we have elections in hand elif next_elec_i and ((gap <= (next_elec_length / 2)) or (gap > (next_elec_length * 1.5))): next_elec = next[sid].pop(next_elec_i) next_elec.start = running_time running_time += next_elec.length() next[sid].insert(i, next_elec) # The next event is better off aligned else: next[sid][i].start = running_time running_time += next[sid][i].length() i += 1 needed_elecs = config.get_station(sid, "num_planned_elections") - num_elections log.debug("create_elec", "Before: Needed elecs: %s // Unused elecs: %s // Current num elections: %s // Next size: %s" % (needed_elecs, len(unused_elecs), num_elections, len(next[sid]))) # Step 5: If we're at less than X elections available, create them (or use unused ones) and append them # No timing is required here, since we're simply outright appending to the end # (any elections appearing before a scheduled item would be handled by the block above) failures = 0 while needed_elecs > 0 and failures <= 2: next_elec = None if len(unused_elecs) > 0: next_elec = unused_elecs.pop(0) else: next_elec = _create_election(sid, running_time) next_elec_length = next_elec.length() if next_elec_length > 0: next_elec.start = running_time running_time += next_elec.length() next[sid].append(next_elec) num_elections += 1 needed_elecs -= 1 else: log.error("create_elec", "Election ID %s was faulty - zero length. Deleting.") next_elec.delete() failures += 1 if failures >= 2: log.error("create_elec", "Total failure when creating elections.") log.debug("create_elec", "After: Unused elecs: %s // Current num elections: %s // Next size: %s" % (len(unused_elecs), num_elections, len(next[sid])))
def _create_elections(sid): # Step, er, 0: Update the request cache first, so elections have the most recent data to work with # (the entire requests module depends on its caches) request.update_cache(sid)