Projet

Général

Profil

0003-virtual-agendas-balance-fill-rate-40056.patch

Emmanuel Cazenave, 16 mars 2020 17:53

Télécharger (8,4 ko)

Voir les différences:

Subject: [PATCH 3/8] virtual agendas: balance fill rate (#40056)

 chrono/api/views.py | 57 ++++++++++++++++++++++++++-----
 tests/test_api.py   | 81 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+), 8 deletions(-)
chrono/api/views.py
561 561

  
562 562
            # get all free slots and separate them by desk
563 563
            all_slots = get_all_slots(agenda, agenda.get_meetingtype(id_=meeting_type_id))
564
            all_slots = [slot for slot in all_slots if not slot.full]
564
            all_free_slots = [slot for slot in all_slots if not slot.full]
565 565
            datetimes_by_desk = defaultdict(set)
566
            for slot in all_slots:
566
            for slot in all_free_slots:
567 567
                datetimes_by_desk[slot.desk.id].add(slot.start_datetime)
568 568

  
569
            # TODO: fill policy for virtual agendas
570
            # search first desk where all requested slots are free
571
            for available_desk_id in sorted(datetimes_by_desk.keys()):
572
                if datetimes.issubset(datetimes_by_desk[available_desk_id]):
573
                    available_desk = Desk.objects.get(id=available_desk_id)
574
                    break
569
            available_desk = None
570

  
571
            if agenda.kind == 'virtual':
572
                # Compute fill_rate by agenda/date
573
                fill_rates = defaultdict(dict)
574
                for slot in all_slots:
575
                    ref_date = slot.start_datetime.date()
576
                    if ref_date not in fill_rates[slot.desk.agenda]:
577
                        date_dict = fill_rates[slot.desk.agenda][ref_date] = {'free': 0, 'full': 0}
578
                    else:
579
                        date_dict = fill_rates[slot.desk.agenda][ref_date]
580
                    if slot.full:
581
                        date_dict['full'] += 1
582
                    else:
583
                        date_dict['free'] += 1
584
                for dd in fill_rates.values():
585
                    for date_dict in dd.values():
586
                        date_dict['fill_rate'] = date_dict['full'] / (date_dict['full'] + date_dict['free'])
587

  
588
                # select a desk on the agenda with min fill_rate on the given date
589
                for available_desk_id in sorted(datetimes_by_desk.keys()):
590
                    if datetimes.issubset(datetimes_by_desk[available_desk_id]):
591
                        desk = Desk.objects.get(id=available_desk_id)
592
                        if available_desk is None:
593
                            available_desk = desk
594
                            available_desk_rate = 0
595
                            for dt in datetimes:
596
                                available_desk_rate += fill_rates[available_desk.agenda][dt.date()][
597
                                    'fill_rate'
598
                                ]
599
                        else:
600
                            for dt in datetimes:
601
                                desk_rate = 0
602
                                for dt in datetimes:
603
                                    desk_rate += fill_rates[desk.agenda][dt.date()]['fill_rate']
604
                            if desk_rate < available_desk_rate:
605
                                available_desk = desk
606
                                available_desk_rate = desk_rate
607

  
575 608
            else:
609
                # meeting agenda
610
                # search first desk where all requested slots are free
611
                for available_desk_id in sorted(datetimes_by_desk.keys()):
612
                    if datetimes.issubset(datetimes_by_desk[available_desk_id]):
613
                        available_desk = Desk.objects.get(id=available_desk_id)
614
                        break
615

  
616
            if available_desk is None:
576 617
                return Response(
577 618
                    {
578 619
                        'err': 1,
tests/test_api.py
2703 2703
    assert resp_booking.json['err'] == 1
2704 2704
    assert resp_booking.json['err_class'] == 'no more desk available'
2705 2705
    assert resp_booking.json['err_desc'] == 'no more desk available'
2706

  
2707

  
2708
def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
2709
    foo_agenda = Agenda.objects.create(
2710
        label='Foo Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
2711
    )
2712
    MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
2713
    foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
2714
    foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2')
2715
    TimePeriod.objects.create(
2716
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
2717
    )
2718
    TimePeriod.objects.create(
2719
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2,
2720
    )
2721

  
2722
    bar_agenda = Agenda.objects.create(
2723
        label='Bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
2724
    )
2725
    MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30)
2726
    bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
2727
    bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2')
2728
    bar_desk_3 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
2729
    bar_desk_4 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
2730
    TimePeriod.objects.create(
2731
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
2732
    )
2733
    TimePeriod.objects.create(
2734
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2,
2735
    )
2736
    TimePeriod.objects.create(
2737
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_3,
2738
    )
2739
    TimePeriod.objects.create(
2740
        weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_4,
2741
    )
2742

  
2743
    virt_agenda = Agenda.objects.create(
2744
        label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=5
2745
    )
2746
    VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
2747
    VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda)
2748
    virt_meeting_type = virt_agenda.iter_meetingtypes()[0]
2749
    # We are saturday and we can book for next monday and tuesday, 4 slots available each day
2750
    api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virt_agenda.slug, virt_meeting_type.slug)
2751
    resp = app.get(api_url)
2752
    # We are saturday and we can book for next monday, 4 slots available each day
2753
    assert len(resp.json['data']) == 4
2754

  
2755
    # there are 6 desks so we can make 6 bookings on the same slot
2756
    fillslot_url = resp.json['data'][0]['api']['fillslot_url']
2757
    app.authorization = ('Basic', ('john.doe', 'password'))
2758
    for i in range(1, 7):
2759
        foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
2760
        bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
2761
        foo_fill_rate = foo_num_bookings / 2
2762
        bar_fill_rate = bar_num_bookings / 4
2763
        next_agenda = None
2764
        if i != 1:
2765
            if foo_fill_rate < bar_fill_rate:
2766
                next_agenda = foo_agenda
2767
            elif foo_fill_rate > bar_fill_rate:
2768
                next_agenda = bar_agenda
2769
            elif foo_fill_rate == bar_fill_rate:
2770
                next_agenda = None
2771

  
2772
        resp_booking = app.post(fillslot_url)
2773
        assert Booking.objects.count() == i
2774
        booking = Booking.objects.get(pk=resp_booking.json['booking_id'])
2775
        assert (
2776
            resp_booking.json['datetime']
2777
            == localtime(booking.event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
2778
            == resp.json['data'][0]['datetime']
2779
        )
2780
        if next_agenda:
2781
            assert booking.event.agenda == next_agenda
2782

  
2783
    foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
2784
    bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
2785
    assert foo_num_bookings == 2
2786
    assert bar_num_bookings == 4
2706
-