Add capacity optimiser to automatic scheduler.
- Legacy-Id: 17905
This commit is contained in:
parent
fc2693370e
commit
a0b6c6dcfc
|
@ -56,6 +56,8 @@ class ScheduleHandler(object):
|
||||||
self.stdout.write('Remaining violations:')
|
self.stdout.write('Remaining violations:')
|
||||||
for v in violations:
|
for v in violations:
|
||||||
self.stdout.write(v)
|
self.stdout.write(v)
|
||||||
|
|
||||||
|
self.schedule.optimise_timeslot_capacity()
|
||||||
|
|
||||||
self._save_schedule(cost)
|
self._save_schedule(cost)
|
||||||
return violations, cost
|
return violations, cost
|
||||||
|
@ -339,6 +341,34 @@ class Schedule(object):
|
||||||
self.stdout.write('Moved {} to random new slot, previously in slot was {}'
|
self.stdout.write('Moved {} to random new slot, previously in slot was {}'
|
||||||
.format(rescheduling_session.group, switched_group))
|
.format(rescheduling_session.group, switched_group))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def optimise_timeslot_capacity(self):
|
||||||
|
"""
|
||||||
|
Optimise the schedule for room capacity usage.
|
||||||
|
|
||||||
|
For each fully overlapping timeslot, the sessions are re-ordered so
|
||||||
|
that smaller sessions are in smaller rooms, and larger sessions in
|
||||||
|
larger rooms. This does not change which sessions overlap, so it
|
||||||
|
has no impact on the schedule cost.
|
||||||
|
"""
|
||||||
|
optimised_timeslots = set()
|
||||||
|
for timeslot in list(self.schedule.keys()):
|
||||||
|
if timeslot in optimised_timeslots:
|
||||||
|
continue
|
||||||
|
timeslot_overlaps = sorted(timeslot.full_overlaps, key=lambda t: t.capacity, reverse=True)
|
||||||
|
sessions_overlaps = [self.schedule.get(t) for t in timeslot_overlaps]
|
||||||
|
sessions_overlaps.sort(key=lambda s: s.attendees if s else 0, reverse=True)
|
||||||
|
assert len(timeslot_overlaps) == len(sessions_overlaps)
|
||||||
|
|
||||||
|
for new_timeslot in timeslot_overlaps:
|
||||||
|
new_session = sessions_overlaps.pop(0)
|
||||||
|
if not new_session and new_timeslot in self.schedule:
|
||||||
|
del self.schedule[new_timeslot]
|
||||||
|
elif new_session:
|
||||||
|
self.schedule[new_timeslot] = new_session
|
||||||
|
|
||||||
|
optimised_timeslots.add(timeslot)
|
||||||
|
optimised_timeslots.update(timeslot_overlaps)
|
||||||
|
|
||||||
def _schedule_session(self, session, timeslot):
|
def _schedule_session(self, session, timeslot):
|
||||||
self.schedule[timeslot] = session
|
self.schedule[timeslot] = session
|
||||||
|
@ -418,13 +448,17 @@ class TimeSlot(object):
|
||||||
self.time_of_day = 'afternoon-late'
|
self.time_of_day = 'afternoon-late'
|
||||||
self.time_group = self.day + '-' + self.time_of_day
|
self.time_group = self.day + '-' + self.time_of_day
|
||||||
self.overlaps = set()
|
self.overlaps = set()
|
||||||
|
self.full_overlaps = set()
|
||||||
self.adjacent = set()
|
self.adjacent = set()
|
||||||
|
|
||||||
def store_relations(self, other_timeslots):
|
def store_relations(self, other_timeslots):
|
||||||
"""
|
"""
|
||||||
Store relations to all other timeslots. This should be called
|
Store relations to all other timeslots. This should be called
|
||||||
after all TimeSlot objects have been created. This allows fast
|
after all TimeSlot objects have been created. This allows fast
|
||||||
lookups of which TimeSlot objects overlap or are adjacent.
|
lookups of which TimeSlot objects overlap or are adjacent.
|
||||||
|
Note that there is a distinction between an overlap, meaning
|
||||||
|
at least part of the timeslots occur during the same time,
|
||||||
|
and a full overlap, meaning the start and end time are identical.
|
||||||
"""
|
"""
|
||||||
for other in other_timeslots:
|
for other in other_timeslots:
|
||||||
if any([
|
if any([
|
||||||
|
@ -433,6 +467,8 @@ class TimeSlot(object):
|
||||||
self.start >= other.start and self.end <= other.end,
|
self.start >= other.start and self.end <= other.end,
|
||||||
]) and other != self:
|
]) and other != self:
|
||||||
self.overlaps.add(other)
|
self.overlaps.add(other)
|
||||||
|
if self.start == other.start and self.end == other.end and other != self:
|
||||||
|
self.full_overlaps.add(other)
|
||||||
if (
|
if (
|
||||||
abs(self.start - other.end) <= datetime.timedelta(minutes=30) or
|
abs(self.start - other.end) <= datetime.timedelta(minutes=30) or
|
||||||
abs(other.start - self.end) <= datetime.timedelta(minutes=30)
|
abs(other.start - self.end) <= datetime.timedelta(minutes=30)
|
||||||
|
|
Loading…
Reference in a new issue