virtual agendas: balance fill rate (#40056)
This commit is contained in:
parent
e2f041511f
commit
06f542f851
|
@ -561,18 +561,59 @@ class Fillslots(APIView):
|
|||
|
||||
# get all free slots and separate them by desk
|
||||
all_slots = get_all_slots(agenda, agenda.get_meetingtype(id_=meeting_type_id))
|
||||
all_slots = [slot for slot in all_slots if not slot.full]
|
||||
all_free_slots = [slot for slot in all_slots if not slot.full]
|
||||
datetimes_by_desk = defaultdict(set)
|
||||
for slot in all_slots:
|
||||
for slot in all_free_slots:
|
||||
datetimes_by_desk[slot.desk.id].add(slot.start_datetime)
|
||||
|
||||
# TODO: fill policy for virtual agendas
|
||||
# search first desk where all requested slots are free
|
||||
for available_desk_id in sorted(datetimes_by_desk.keys()):
|
||||
if datetimes.issubset(datetimes_by_desk[available_desk_id]):
|
||||
available_desk = Desk.objects.get(id=available_desk_id)
|
||||
break
|
||||
available_desk = None
|
||||
|
||||
if agenda.kind == 'virtual':
|
||||
# Compute fill_rate by agenda/date
|
||||
fill_rates = defaultdict(dict)
|
||||
for slot in all_slots:
|
||||
ref_date = slot.start_datetime.date()
|
||||
if ref_date not in fill_rates[slot.desk.agenda]:
|
||||
date_dict = fill_rates[slot.desk.agenda][ref_date] = {'free': 0, 'full': 0}
|
||||
else:
|
||||
date_dict = fill_rates[slot.desk.agenda][ref_date]
|
||||
if slot.full:
|
||||
date_dict['full'] += 1
|
||||
else:
|
||||
date_dict['free'] += 1
|
||||
for dd in fill_rates.values():
|
||||
for date_dict in dd.values():
|
||||
date_dict['fill_rate'] = date_dict['full'] / (date_dict['full'] + date_dict['free'])
|
||||
|
||||
# select a desk on the agenda with min fill_rate on the given date
|
||||
for available_desk_id in sorted(datetimes_by_desk.keys()):
|
||||
if datetimes.issubset(datetimes_by_desk[available_desk_id]):
|
||||
desk = Desk.objects.get(id=available_desk_id)
|
||||
if available_desk is None:
|
||||
available_desk = desk
|
||||
available_desk_rate = 0
|
||||
for dt in datetimes:
|
||||
available_desk_rate += fill_rates[available_desk.agenda][dt.date()][
|
||||
'fill_rate'
|
||||
]
|
||||
else:
|
||||
for dt in datetimes:
|
||||
desk_rate = 0
|
||||
for dt in datetimes:
|
||||
desk_rate += fill_rates[desk.agenda][dt.date()]['fill_rate']
|
||||
if desk_rate < available_desk_rate:
|
||||
available_desk = desk
|
||||
available_desk_rate = desk_rate
|
||||
|
||||
else:
|
||||
# meeting agenda
|
||||
# search first desk where all requested slots are free
|
||||
for available_desk_id in sorted(datetimes_by_desk.keys()):
|
||||
if datetimes.issubset(datetimes_by_desk[available_desk_id]):
|
||||
available_desk = Desk.objects.get(id=available_desk_id)
|
||||
break
|
||||
|
||||
if available_desk is None:
|
||||
return Response(
|
||||
{
|
||||
'err': 1,
|
||||
|
|
|
@ -2874,3 +2874,84 @@ def test_virtual_agendas_meetings_booking(app, mock_now, user):
|
|||
assert resp_booking.json['err'] == 1
|
||||
assert resp_booking.json['err_class'] == 'no more desk available'
|
||||
assert resp_booking.json['err_desc'] == 'no more desk available'
|
||||
|
||||
|
||||
def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
|
||||
foo_agenda = Agenda.objects.create(
|
||||
label='Foo Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
||||
)
|
||||
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
||||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2,
|
||||
)
|
||||
|
||||
bar_agenda = Agenda.objects.create(
|
||||
label='Bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
||||
)
|
||||
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30)
|
||||
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
||||
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2')
|
||||
bar_desk_3 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
||||
bar_desk_4 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_3,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_4,
|
||||
)
|
||||
|
||||
virt_agenda = Agenda.objects.create(
|
||||
label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=5
|
||||
)
|
||||
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
|
||||
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda)
|
||||
virt_meeting_type = virt_agenda.iter_meetingtypes()[0]
|
||||
# We are saturday and we can book for next monday and tuesday, 4 slots available each day
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virt_agenda.slug, virt_meeting_type.slug)
|
||||
resp = app.get(api_url)
|
||||
# We are saturday and we can book for next monday, 4 slots available each day
|
||||
assert len(resp.json['data']) == 4
|
||||
|
||||
# there are 6 desks so we can make 6 bookings on the same slot
|
||||
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
for i in range(1, 7):
|
||||
foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
|
||||
bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
|
||||
foo_fill_rate = foo_num_bookings / 2
|
||||
bar_fill_rate = bar_num_bookings / 4
|
||||
next_agenda = None
|
||||
if i != 1:
|
||||
if foo_fill_rate < bar_fill_rate:
|
||||
next_agenda = foo_agenda
|
||||
elif foo_fill_rate > bar_fill_rate:
|
||||
next_agenda = bar_agenda
|
||||
elif foo_fill_rate == bar_fill_rate:
|
||||
next_agenda = None
|
||||
|
||||
resp_booking = app.post(fillslot_url)
|
||||
assert Booking.objects.count() == i
|
||||
booking = Booking.objects.get(pk=resp_booking.json['booking_id'])
|
||||
assert (
|
||||
resp_booking.json['datetime']
|
||||
== localtime(booking.event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
== resp.json['data'][0]['datetime']
|
||||
)
|
||||
if next_agenda:
|
||||
assert booking.event.agenda == next_agenda
|
||||
|
||||
foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
|
||||
bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
|
||||
assert foo_num_bookings == 2
|
||||
assert bar_num_bookings == 4
|
||||
|
|
Loading…
Reference in New Issue