Projet

Général

Profil

0001-api-handle-multiple-slots-booking-16238.patch

Thomas Noël, 04 avril 2018 12:42

Télécharger (6,33 ko)

Voir les différences:

Subject: [PATCH] api: handle multiple slots booking (#16238)

 chrono/api/urls.py  |  2 ++
 chrono/api/views.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/test_api.py   |  1 +
 3 files changed, 98 insertions(+), 1 deletion(-)
chrono/api/urls.py
27 27
        views.fillslot, name='api-fillslot'),
28 28
    url(r'agenda/(?P<agenda_identifier>[\w-]+)/status/(?P<event_pk>\w+)/$', views.slot_status,
29 29
        name='api-event-status'),
30
    url(r'agenda/(?P<agenda_identifier>[\w-]+)/fillslots/$',
31
        views.fillslots, name='api-agenda-fillslots'),
30 32

  
31 33
    url(r'agenda/meetings/(?P<meeting_identifier>[\w-]+)/datetimes/$',
32 34
        views.meeting_datetimes, name='api-agenda-meeting-datetimes-legacy'),
chrono/api/views.py
110 110
                        kwargs={'agenda_identifier': agenda.slug})),
111 111
            'desks_url': request.build_absolute_uri(
112 112
                reverse('api-agenda-desks',
113
                        kwargs={'agenda_identifier': agenda.slug}))
113
                        kwargs={'agenda_identifier': agenda.slug})),
114
            'fillslots_url': request.build_absolute_uri(
115
                reverse('api-agenda-fillslots',
116
                        kwargs={'agenda_identifier': agenda.slug})),
114 117
        }
115 118

  
116 119
    return agenda_detail
......
408 411
fillslot = Fillslot.as_view()
409 412

  
410 413

  
414
class Fillslots(GenericAPIView):
415
    serializer_class = SlotSerializer
416
    permission_classes = (permissions.IsAuthenticated,)
417

  
418
    def post(self, request, agenda_identifier=None, format=None):
419
        try:
420
            agenda = Agenda.objects.get(slug=agenda_identifier)
421
        except Agenda.DoesNotExist:
422
            try:
423
                # legacy access by agenda id
424
                agenda = Agenda.objects.get(id=int(agenda_identifier))
425
            except (ValueError, Agenda.DoesNotExist):
426
                raise Http404()
427
        if agenda.kind != 'meetings':
428
            raise Http404('agenda found, but it was not a meetings agenda')
429

  
430
        slots = request.data.get('slots')
431
        if not slots:
432
            return Response({'err': 1, 'reason': 'missing slots'})
433
        if not isinstance(slots, list):
434
            return Response({'err': 1, 'reason': 'slots must be a list'})
435

  
436
        # slots came from fake event_pk (meeting_type:start_datetime)
437
        meeting_type_id = slots[0].split(':')[0]
438
        datetimes = set()
439
        for slot in slots:
440
            meeting_type_id_, datetime_str = slots.split(':')
441
            if meeting_type_id_ != meeting_type_id:
442
                return Response({
443
                    'err': 1,
444
                    'reason': 'all slots must have the same meeting type id (%s)' % meeting_type_id
445
                })
446
            datetimes.add(make_aware(datetime.datetime.strptime(datetime_str, '%Y-%m-%d-%H%M')))
447

  
448
        # get all free slots and separate them by desk
449
        all_slots = get_all_slots(agenda, MeetingType.objects.get(id=meeting_type_id))
450
        all_slots = [slot for slot in all_slots if not slot.full]
451
        datetimes_by_desk = defaultdict(set)
452
        for slot in all_slots:
453
            datetimes_by_desk[slot.desk.id].add(slot.start_datetime)
454

  
455
        # search first desk where all requested slots are free
456
        for available_desk_id in datetimes_by_desk:
457
            if datetimes.issubset(datetimes_by_desk[available_desk_id]):
458
                available_desk = Desk.objects.filter(id=available_desk_id)[0]
459
                break
460
        else:
461
            return Response({'err': 1, 'reason': 'no more desk available'})
462

  
463
        # all datetimes are free, book them in order
464
        datetimes = list(datetimes)
465
        datetimes.sort()
466

  
467
        # create bookings and relative events (booking requires a real Event object)
468
        first_booking = None
469
        for start_datetime in datetimes:
470
            event = Event.objects.create(agenda=agenda,
471
                    meeting_type_id=meeting_type_id,
472
                    start_datetime=start_datetime,
473
                    full=False, places=1,
474
                    desk=available_desk)
475
            booking = Booking(event_id=event_pk, extra_data=request.data)
476
            for attr in ('label', 'user_name', 'backoffice_url'):
477
                if isinstance(request.data.get(attr), basestring):
478
                    setattr(booking, attr, request.data.get(attr))
479
            if first_booking is not None:
480
                additional_booking.primary_booking = first_booking
481
            booking.save()
482
            if first_booking is None:
483
                first_booking = booking
484

  
485
        response = {
486
            'err': 0,
487
            'in_waiting_list': 0,
488
            'booking_id': first_booking.id,
489
            'datetime': localtime(datetimes[0]),
490
            'end_datetime': localtime(event.end_datetime),  # it's the last created one
491
            'desk': {
492
                'label': available_desk.label,
493
                'slug': available_desk.slug,
494
            },
495
            'api': {
496
                'cancel_url': request.build_absolute_uri(
497
                    reverse('api-cancel-booking', kwargs={'booking_pk': first_booking.id}))
498
            }
499
        }
500

  
501

  
502
fillslots = Fillslots.as_view()
503

  
504

  
411 505
class BookingAPI(APIView):
412 506
    permission_classes = (permissions.IsAuthenticated,)
413 507

  
tests/test_api.py
105 105
         'kind': 'meetings',
106 106
         'api': {'meetings_url': 'http://testserver/api/agenda/%s/meetings/' % meetings_agenda.slug,
107 107
                 'desks_url': 'http://testserver/api/agenda/%s/desks/' % meetings_agenda.slug,
108
                 'fillslots_url': 'http://testserver/api/agenda/%s/fillslots/' % meetings_agenda.slug,
108 109
                },
109 110
        },
110 111
        {'text': 'Foo bar2', 'id': u'foo-bar2', 'kind': 'events', 'slug': 'foo-bar2',
111
-