Improved initial session scheduling for tight schedules
- Legacy-Id: 17904
This commit is contained in:
parent
2c49e7b2dd
commit
fc2693370e
|
@ -140,7 +140,11 @@ class Schedule(object):
|
||||||
"""
|
"""
|
||||||
Check the number of sessions, their required capacity and duration against availability.
|
Check the number of sessions, their required capacity and duration against availability.
|
||||||
If there are too many sessions, the generator exits.
|
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):
|
if len(self.sessions) > len(self.timeslots):
|
||||||
raise CommandError('More sessions ({}) than timeslots ({})'
|
raise CommandError('More sessions ({}) than timeslots ({})'
|
||||||
|
@ -218,6 +222,9 @@ class Schedule(object):
|
||||||
- Second: shortest duration that still fits
|
- Second: shortest duration that still fits
|
||||||
- Third: smallest room that still fits
|
- Third: smallest room that still fits
|
||||||
If there are multiple options with equal value, a random one is picked.
|
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:
|
if self.verbosity >= 2:
|
||||||
self.stdout.write('== Initial scheduler starting, scheduling {} sessions in {} timeslots =='
|
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)
|
sessions = sorted(self.sessions, key=lambda s: s.complexity, reverse=True)
|
||||||
|
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
possible_slots = [t for t in self.timeslots if
|
possible_slots = [t for t in self.timeslots if t not in self.schedule.keys()]
|
||||||
t not in self.schedule.keys() and session.fits_in_timeslot(t)]
|
|
||||||
random.shuffle(possible_slots)
|
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):
|
def timeslot_preference(t):
|
||||||
proposed_schedule = self.schedule.copy()
|
proposed_schedule = self.schedule.copy()
|
||||||
proposed_schedule[t] = session
|
proposed_schedule[t] = session
|
||||||
|
@ -339,8 +341,6 @@ class Schedule(object):
|
||||||
break
|
break
|
||||||
|
|
||||||
def _schedule_session(self, session, timeslot):
|
def _schedule_session(self, session, timeslot):
|
||||||
if not session.fits_in_timeslot(timeslot):
|
|
||||||
raise ValueError
|
|
||||||
self.schedule[timeslot] = session
|
self.schedule[timeslot] = session
|
||||||
|
|
||||||
def _cost_for_switch(self, timeslot1, timeslot2):
|
def _cost_for_switch(self, timeslot1, timeslot2):
|
||||||
|
@ -555,6 +555,14 @@ class Session(object):
|
||||||
violations, cost = [], 0
|
violations, cost = [], 0
|
||||||
overlapping_sessions = tuple(overlapping_sessions)
|
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:
|
if my_timeslot.time_group in self.timeranges_unavailable:
|
||||||
violations.append('{}: scheduled in unavailable timerange {}'
|
violations.append('{}: scheduled in unavailable timerange {}'
|
||||||
.format(self.group, my_timeslot.time_group))
|
.format(self.group, my_timeslot.time_group))
|
||||||
|
|
Loading…
Reference in a new issue