From fc2693370ecaaf5fcfa0823c861db7f1411c6dd9 Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Mon, 1 Jun 2020 12:26:21 +0000 Subject: [PATCH] Improved initial session scheduling for tight schedules - Legacy-Id: 17904 --- .../management/commands/schedule_generator.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ietf/meeting/management/commands/schedule_generator.py b/ietf/meeting/management/commands/schedule_generator.py index b61ded419..2c6624fc4 100644 --- a/ietf/meeting/management/commands/schedule_generator.py +++ b/ietf/meeting/management/commands/schedule_generator.py @@ -140,7 +140,11 @@ class Schedule(object): """ Check the number of sessions, their required capacity and duration against availability. If there are too many sessions, the generator exits. - If sessions can't fit, they are trimmed, and a fixed cost is applied. + If sessions can't fit, they are trimmed, and a fixed cost is applied. + + Note that the trim is only applied on the in-memory object. The purpose + of trimming in advance is to prevent the optimiser from trying to resolve + a constraint that can never be resolved. """ if len(self.sessions) > len(self.timeslots): raise CommandError('More sessions ({}) than timeslots ({})' @@ -218,6 +222,9 @@ class Schedule(object): - Second: shortest duration that still fits - Third: smallest room that still fits If there are multiple options with equal value, a random one is picked. + + For initial scheduling, it is not a hard requirement that the timeslot is long + or large enough, though that will be preferred due to the lower cost. """ if self.verbosity >= 2: self.stdout.write('== Initial scheduler starting, scheduling {} sessions in {} timeslots ==' @@ -225,14 +232,9 @@ class Schedule(object): sessions = sorted(self.sessions, key=lambda s: s.complexity, reverse=True) for session in sessions: - possible_slots = [t for t in self.timeslots if - t not in self.schedule.keys() and session.fits_in_timeslot(t)] + possible_slots = [t for t in self.timeslots if t not in self.schedule.keys()] random.shuffle(possible_slots) - if not len(possible_slots): - # TODO: this needs better handling - raise Exception('No timeslot was left for {} ({})' - .format(session.group, session.session_pk)) - + def timeslot_preference(t): proposed_schedule = self.schedule.copy() proposed_schedule[t] = session @@ -339,8 +341,6 @@ class Schedule(object): break def _schedule_session(self, session, timeslot): - if not session.fits_in_timeslot(timeslot): - raise ValueError self.schedule[timeslot] = session def _cost_for_switch(self, timeslot1, timeslot2): @@ -555,6 +555,14 @@ class Session(object): violations, cost = [], 0 overlapping_sessions = tuple(overlapping_sessions) + if self.attendees > my_timeslot.capacity: + violations.append('{}: scheduled scheduled in too small room'.format(self.group)) + cost += self.business_constraint_costs['session_requires_trim'] + + if self.requested_duration > my_timeslot.duration: + violations.append('{}: scheduled scheduled in too short timeslot'.format(self.group)) + cost += self.business_constraint_costs['session_requires_trim'] + if my_timeslot.time_group in self.timeranges_unavailable: violations.append('{}: scheduled in unavailable timerange {}' .format(self.group, my_timeslot.time_group))