Seats API: Add is_available filter (Z#23163419) (#4409)

* Seats API: Add is_available filter (Z#23163419)

* docs
This commit is contained in:
Raphael Michel
2024-08-21 17:43:13 +02:00
committed by GitHub
parent b96374fcf6
commit a6f93b6cf0
4 changed files with 62 additions and 15 deletions

View File

@@ -671,12 +671,31 @@ class EventSettingsView(views.APIView):
return Response(s.data)
class SeatFilter(FilterSet):
is_available = django_filters.BooleanFilter(method="is_available_qs")
def is_available_qs(self, queryset, name, value):
expr = (
Q(orderposition_id__isnull=True, cartposition_id__isnull=True, voucher_id__isnull=True)
)
if self.request.event.settings.seating_minimal_distance:
expr = expr & Q(has_closeby_taken=False)
if value:
return queryset.filter(expr)
else:
return queryset.exclude(expr)
class Meta:
model = Seat
fields = ('zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'blocked',)
class SeatViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = SeatSerializer
queryset = Seat.objects.none()
write_permission = 'can_change_event_settings'
filter_backends = (DjangoFilterBackend,)
filterset_fields = ('zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'blocked',)
filter_backends = (DjangoFilterBackend, )
filterset_class = SeatFilter
def get_queryset(self):
if self.request.event.has_subevents and 'subevent' in self.request.resolver_match.kwargs:
@@ -684,9 +703,23 @@ class SeatViewSet(ConditionalListView, viewsets.ModelViewSet):
subevent = self.request.event.subevents.get(pk=self.request.resolver_match.kwargs['subevent'])
except SubEvent.DoesNotExist:
raise NotFound('Subevent not found')
qs = Seat.annotated(event_id=self.request.event.id, subevent=subevent, qs=subevent.seats.all(), annotate_ids=True)
qs = Seat.annotated(
event_id=self.request.event.id,
subevent=subevent,
qs=subevent.seats.all(),
annotate_ids=True,
minimal_distance=self.request.event.settings.seating_minimal_distance,
distance_only_within_row=self.request.event.settings.seating_distance_only_within_row,
)
elif not self.request.event.has_subevents and 'subevent' not in self.request.resolver_match.kwargs:
qs = Seat.annotated(event_id=self.request.event.id, subevent=None, qs=self.request.event.seats.all(), annotate_ids=True)
qs = Seat.annotated(
event_id=self.request.event.id,
subevent=None,
qs=self.request.event.seats.all(),
annotate_ids=True,
minimal_distance=self.request.event.settings.seating_minimal_distance,
distance_only_within_row=self.request.event.settings.seating_distance_only_within_row,
)
else:
raise NotFound('Please use the subevent-specific endpoint' if self.request.event.has_subevents
else 'This event has no subevents')

View File

@@ -242,7 +242,11 @@ class Seat(models.Model):
Power(F('y') - OuterRef('y'), Value(2), output_field=models.FloatField())
)
).filter(
Q(has_order=True) | Q(has_cart=True) | Q(has_voucher=True),
(
(Q(orderposition_id__isnull=False) | Q(cartposition_id__isnull=False) | Q(voucher_id__isnull=False))
if annotate_ids else
(Q(has_order=True) | Q(has_cart=True) | Q(has_voucher=True))
),
distance__lt=minimal_distance ** 2
)
if distance_only_within_row:

View File

@@ -1589,7 +1589,9 @@ def test_event_block_unblock_seat(token_client, organizer, event, seatingplan, i
@pytest.mark.django_db
def test_event_expand_seat_querycount(token_client, organizer, event, seatingplan, item):
def test_event_expand_seat_filter_and_querycount(token_client, organizer, event, seatingplan, item):
event.settings.seating_minimal_distance = 2
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
@@ -1603,9 +1605,9 @@ def test_event_expand_seat_querycount(token_client, organizer, event, seatingpla
assert resp.status_code == 200
event.refresh_from_db()
with assert_num_queries(9):
with assert_num_queries(12):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
'?expand=orderposition&expand=cartposition&expand=voucher&is_available=true'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 3
@@ -1613,24 +1615,31 @@ def test_event_expand_seat_querycount(token_client, organizer, event, seatingpla
with scope(organizer=organizer):
v0 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-0'))
with assert_num_queries(10):
with assert_num_queries(13):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
'?expand=orderposition&expand=cartposition&expand=voucher&is_available=false'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 1
assert resp.data['results'][0]['voucher']['id'] == v0.pk
assert resp.data['results'][1]['voucher'] is None
assert resp.data['results'][2]['voucher'] is None
with assert_num_queries(12):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher&is_available=true'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 2
with scope(organizer=organizer):
v1 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-1'))
v2 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-2'))
with assert_num_queries(10):
with assert_num_queries(13):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
'?expand=orderposition&expand=cartposition&expand=voucher&is_available=false'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 3
assert resp.data['results'][0]['voucher']['id'] == v0.pk
assert resp.data['results'][1]['voucher']['id'] == v1.pk
assert resp.data['results'][2]['voucher']['id'] == v2.pk