Projet

Général

Profil

0002-api-endpoint-to-resize-a-booking-40039.patch

Lauréline Guérin, 17 mars 2020 15:01

Télécharger (12,2 ko)

Voir les différences:

Subject: [PATCH 2/2] api: endpoint to resize a booking (#40039)

 chrono/agendas/models.py |  15 +++++
 chrono/api/urls.py       |   1 +
 chrono/api/views.py      | 100 +++++++++++++++++++++++++++++++++
 tests/test_api.py        | 117 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 233 insertions(+)
chrono/agendas/models.py
507 507
        ics.add(vevent)
508 508
        return ics.serialize()
509 509

  
510
    def clone(self, in_waiting_list=False, primary_booking=None, save=True):
511
        new_booking = Booking(
512
            primary_booking=primary_booking,
513
            event_id=self.event_id,
514
            in_waiting_list=in_waiting_list,
515
            label=self.label,
516
            user_name=self.user_name,
517
            backoffice_url=self.backoffice_url,
518
            user_display_label=self.user_display_label,
519
            extra_data=self.extra_data,
520
        )
521
        if save:
522
            new_booking.save()
523
        return new_booking
524

  
510 525

  
511 526
class Desk(models.Model):
512 527
    agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
chrono/api/urls.py
49 49
    url(r'^booking/(?P<booking_pk>\w+)/cancel/$', views.cancel_booking, name='api-cancel-booking'),
50 50
    url(r'^booking/(?P<booking_pk>\w+)/accept/$', views.accept_booking, name='api-accept-booking'),
51 51
    url(r'^booking/(?P<booking_pk>\w+)/suspend/$', views.suspend_booking, name='api-suspend-booking'),
52
    url(r'^booking/(?P<booking_pk>\w+)/resize/$', views.resize_booking, name='api-resize-booking'),
52 53
    url(r'^booking/(?P<booking_pk>\w+)/ics/$', views.booking_ics, name='api-booking-ics'),
53 54
]
chrono/api/views.py
845 845
suspend_booking = SuspendBooking.as_view()
846 846

  
847 847

  
848
class ResizeSerializer(serializers.Serializer):
849
    count = serializers.IntegerField(min_value=1)
850

  
851

  
852
class ResizeBooking(APIView):
853
    '''
854
    Resize a booking.
855

  
856
    It will return error codes if the booking was cancelled before (code 1)
857
    if the booking is not primary (code 2)
858
    if the event is sold out (code 3) or
859
    if the booking is on multi events (code 4).
860
    '''
861

  
862
    permission_classes = (permissions.IsAuthenticated,)
863
    serializer_class = ResizeSerializer
864

  
865
    def post(self, request, booking_pk=None, format=None):
866
        serializer = self.serializer_class(data=request.data)
867
        if not serializer.is_valid():
868
            return Response(
869
                {
870
                    'err': 1,
871
                    'err_class': 'invalid payload',
872
                    'err_desc': _('invalid payload'),
873
                    'errors': serializer.errors,
874
                },
875
                status=status.HTTP_400_BAD_REQUEST,
876
            )
877
        payload = serializer.validated_data
878

  
879
        booking = get_object_or_404(Booking, pk=booking_pk)
880
        event = booking.event
881
        if booking.cancellation_datetime:
882
            response = {
883
                'err': 1,
884
                'err_class': 'booking is cancelled',
885
                'err_desc': _('booking is cancelled'),
886
            }
887
            return Response(response)
888
        if booking.primary_booking is not None:
889
            response = {
890
                'err': 2,
891
                'err_class': 'secondary booking',
892
                'err_desc': _('secondary booking'),
893
            }
894
            return Response(response)
895
        event_ids = set([event.pk])
896
        secondary_bookings = booking.secondary_booking_set.all().order_by('-creation_datetime')
897
        for secondary in secondary_bookings:
898
            event_ids.add(secondary.event_id)
899
        if len(event_ids) > 1:
900
            response = {
901
                'err': 4,
902
                'err_class': 'can not resize multi event booking',
903
                'err_desc': _('can not resize multi event booking'),
904
            }
905
            return Response(response)
906

  
907
        count = payload['count']
908
        booked_places = event.waiting_list_places and event.waiting_list or event.booked_places
909
        if booked_places < count:
910
            if (
911
                event.waiting_list
912
                and count > event.waiting_list_places
913
                or not event.waiting_list
914
                and count > event.places
915
            ):
916
                response = {
917
                    'err': 3,
918
                    'err_class': 'sold out',
919
                    'err_desc': _('sold out'),
920
                }
921
                return Response(response)
922

  
923
        with transaction.atomic():
924
            if booked_places > count:
925
                # decrease places
926
                for secondary in secondary_bookings[: booked_places - count]:
927
                    secondary.delete()
928
            elif booked_places < count:
929
                # increase places
930
                bulk_bookings = []
931
                for i in range(0, count - booked_places):
932
                    bulk_bookings.append(
933
                        booking.clone(
934
                            in_waiting_list=bool(event.waiting_list_places and event.waiting_list),
935
                            primary_booking=booking,
936
                            save=False,
937
                        )
938
                    )
939
                Booking.objects.bulk_create(bulk_bookings)
940

  
941
        response = {'err': 0, 'booking_id': booking.pk}
942
        return Response(response)
943

  
944

  
945
resize_booking = ResizeBooking.as_view()
946

  
947

  
848 948
class SlotStatus(APIView):
849 949
    permission_classes = (permissions.IsAuthenticated,)
850 950

  
tests/test_api.py
1419 1419
    assert primary.in_waiting_list is False
1420 1420

  
1421 1421

  
1422
def test_resize_booking(app, some_data, user):
1423
    agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id
1424
    event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
1425
    event.places = 5
1426
    event.waiting_list_places = 5
1427
    event.save()
1428
    event2 = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[1]
1429

  
1430
    # create a booking not on the waiting list
1431
    primary = Booking.objects.create(event=event, in_waiting_list=False)
1432
    secondary = Booking.objects.create(event=event, in_waiting_list=False, primary_booking=primary)
1433

  
1434
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1435

  
1436
    app.authorization = ('Basic', ('john.doe', 'password'))
1437

  
1438
    resp = app.post('/api/booking/%s/resize/' % secondary.pk, params={'count': 42})
1439
    assert resp.json['err'] == 2
1440
    assert resp.json['reason'] == 'secondary booking'  # legacy
1441
    assert resp.json['err_class'] == 'secondary booking'
1442
    assert resp.json['err_desc'] == 'secondary booking'
1443

  
1444
    # resize a booking that doesn't exist
1445
    resp = app.post('/api/booking/0/resize/', params={'count': 42}, status=404)
1446

  
1447
    # decrease a booking to 0
1448
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 0}, status=400)
1449
    assert resp.json['err'] == 1
1450

  
1451
    # decrease a booking not in waiting list
1452
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1453
    assert Booking.objects.filter().count() == 2
1454
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1455
    assert resp.json['err'] == 0
1456
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1457
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1458
    assert Booking.objects.filter().count() == 1
1459

  
1460
    # no changes
1461
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1462
    assert resp.json['err'] == 0
1463
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1464
    assert Booking.objects.filter().count() == 1
1465

  
1466
    # increase a booking not in waiting list
1467
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 2})
1468
    assert resp.json['err'] == 0
1469
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1470
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1471
    assert Booking.objects.filter().count() == 2
1472
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1473
    assert resp.json['err'] == 3
1474
    assert resp.json['reason'] == 'sold out'  # legacy
1475
    assert resp.json['err_class'] == 'sold out'
1476
    assert resp.json['err_desc'] == 'sold out'
1477
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 5})
1478
    assert resp.json['err'] == 0
1479
    assert Booking.objects.filter(in_waiting_list=False).count() == 5
1480
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1481
    assert Booking.objects.filter().count() == 5
1482
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1483
    assert resp.json['err'] == 3
1484
    assert resp.json['reason'] == 'sold out'  # legacy
1485
    assert resp.json['err_class'] == 'sold out'
1486
    assert resp.json['err_desc'] == 'sold out'
1487

  
1488
    # decrease a booking in waiting list
1489
    primary.suspend()
1490
    assert Booking.objects.filter(in_waiting_list=True).count() == 5
1491
    assert Booking.objects.filter().count() == 5
1492
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1493
    assert resp.json['err'] == 0
1494
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1495
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1496
    assert Booking.objects.filter().count() == 1
1497

  
1498
    # no changes
1499
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1500
    assert resp.json['err'] == 0
1501
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1502
    assert Booking.objects.filter().count() == 1
1503

  
1504
    # increase a booking in waiting list
1505
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 2})
1506
    assert resp.json['err'] == 0
1507
    assert Booking.objects.filter(in_waiting_list=True).count() == 2
1508
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1509
    assert Booking.objects.filter().count() == 2
1510
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1511
    assert resp.json['err'] == 3
1512
    assert resp.json['reason'] == 'sold out'  # legacy
1513
    assert resp.json['err_class'] == 'sold out'
1514
    assert resp.json['err_desc'] == 'sold out'
1515
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 5})
1516
    assert resp.json['err'] == 0
1517
    assert Booking.objects.filter(in_waiting_list=True).count() == 5
1518
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1519
    assert Booking.objects.filter().count() == 5
1520
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1521
    assert resp.json['err'] == 3
1522
    assert resp.json['reason'] == 'sold out'  # legacy
1523
    assert resp.json['err_class'] == 'sold out'
1524
    assert resp.json['err_desc'] == 'sold out'
1525

  
1526
    # resize a booking that is on multi events
1527
    secondary = Booking.objects.create(event=event2, in_waiting_list=False, primary_booking=primary)
1528
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1529
    assert resp.json['err'] == 4
1530
    assert resp.json['reason'] == 'can not resize multi event booking'  # legacy
1531
    assert resp.json['err_class'] == 'can not resize multi event booking'
1532
    assert resp.json['err_desc'] == 'can not resize multi event booking'
1533
    # resize a booking that was cancelled before
1534
    primary.cancel()
1535
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1536
    assert resp.json['err'] == 1
1537

  
1538

  
1422 1539
def test_multiple_booking_api(app, some_data, user):
1423 1540
    agenda = Agenda.objects.filter(label=u'Foo bar')[0]
1424 1541
    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
1425
-