def manage_next(sid): # pylint: disable=W0612 max_sched_id, max_elec_id, num_elections, max_future_time = _get_schedule_stats(sid) # pylint: enable=W0612 now_producer = get_producer_at_time(sid, timestamp()) next_producer = get_producer_at_time(sid, max_future_time) nextnext_producer_start = db.c.fetch_var( "SELECT sched_start FROM r4_schedule WHERE sid = %s AND sched_used = FALSE AND sched_start > %s AND sched_timed = TRUE", (sid, max_future_time), ) time_to_future_producer = None if nextnext_producer_start: time_to_future_producer = nextnext_producer_start - max_future_time else: time_to_future_producer = 86400 while len(upnext[sid]) < min(now_producer.plan_ahead_limit, next_producer.plan_ahead_limit): target_length = None if time < 20: log.debug("timing", "SID %s <20 seconds to upnext event, not using timing." % sid) if time_to_future_producer < 40: target_length = time_to_future_producer next_producer = rainwave.events.shortest_election.ShortestElectionProducer(sid) log.debug("timing", "SID %s <40 seconds to upnext event, using shortest elections." % sid) elif time_to_future_producer < (playlist.get_average_song_length(sid) * 1.3): target_length = time_to_future_producer log.debug("timing", "SID %s close to event, timing to %s seconds long." % (sid, target_length)) elif time_to_future_producer < (playlist.get_average_song_length(sid) * 2.2): target_length = playlist.get_average_song_length(sid) log.debug("timing", "SID %s has an upcoming event, timing to %s seconds long." % (sid, target_length)) next_event = next_producer.load_next_event(target_length, max_elec_id) if not next_event: log.info( "manage_next", "Producer ID %s type %s did not produce an event." % (next_producer.id, next_producer.type), ) next_producer = election.ElectionProducer(sid) next_event = next_producer.load_next_event(target_length, max_elec_id) upnext[sid].append(next_event) if next_event.is_election: num_elections += 1 if next_event.is_election and next_event.id > max_elec_id: max_elec_id = next_event.id max_future_time += upnext[sid][-1].length() time_to_future_producer -= upnext[sid][-1].length() next_producer = get_producer_at_time(sid, max_future_time) future_time = None if current[sid].start: future_time = current[sid].start + current[sid].length() else: future_time = int(timestamp() + current[sid].length()) for evt in upnext[sid]: evt.start = future_time future_time += evt.length() if evt.is_election: evt.update_vote_counts()
def manage_next(sid): max_sched_id, max_elec_id, num_elections, max_future_time = _get_schedule_stats(sid) now_producer = get_producer_at_time(sid, time.time()) next_producer = get_producer_at_time(sid, max_future_time) nextnext_producer_start = db.c.fetch_var("SELECT sched_start FROM r4_schedule WHERE sid = %s AND sched_used = FALSE AND sched_start > %s AND sched_timed = TRUE", (sid, max_future_time)) time_to_future_producer = None if nextnext_producer_start: time_to_future_producer = nextnext_producer_start - max_future_time else: time_to_future_producer = 86400 while len(upnext[sid]) < min(now_producer.plan_ahead_limit, next_producer.plan_ahead_limit): target_length = None if time < 20: log.debug("timing", "SID %s <20 seconds to upnext event, not using timing." % sid) if time_to_future_producer < 40: target_length = time_to_future_producer next_producer = rainwave.events.shortest_election.ShortestElectionProducer(sid) log.debug("timing", "SID %s <40 seconds to upnext event, using shortest elections." % sid) elif time_to_future_producer < (playlist.get_average_song_length(sid) * 1.3): target_length = time_to_future_producer log.debug("timing", "SID %s close to event, timing to %s seconds long." % (sid, target_length)) elif time_to_future_producer < (playlist.get_average_song_length(sid) * 2.2): target_length = playlist.get_average_song_length(sid) log.debug("timing", "SID %s has an upcoming event, timing to %s seconds long." % (sid, target_length)) next_event = next_producer.load_next_event(target_length, max_elec_id) if not next_event: log.info("manage_next", "Producer ID %s type %s did not produce an event." % (next_producer.id, next_producer.type)) next_producer = election.ElectionProducer(sid) next_event = next_producer.load_next_event(target_length, max_elec_id) upnext[sid].append(next_event) if next_event.is_election: num_elections += 1 if next_event.is_election and next_event.id > max_elec_id: max_elec_id = next_event.id max_future_time += upnext[sid][-1].length() time_to_future_producer -= upnext[sid][-1].length() next_producer = get_producer_at_time(sid, max_future_time) future_time = None if current[sid].start: future_time = current[sid].start + current[sid].length() else: future_time = int(time.time() + current[sid].length()) for evt in upnext[sid]: evt.start = future_time future_time += evt.length()
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) 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])))