mirror of
https://github.com/pretix/pretix.git
synced 2026-04-23 23:22:32 +00:00
API: Add status view to checkin list resource
This commit is contained in:
@@ -44,6 +44,10 @@ include_pending boolean If ``true``, th
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
.. versionchanged:: 1.15
|
||||
|
||||
The ``../status/`` detail endpoint has been added.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
||||
|
||||
Returns a list of all check-in lists within a given event.
|
||||
@@ -128,6 +132,72 @@ Endpoints
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(id)/status/
|
||||
|
||||
Returns detailed status information on a check-in list, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/status/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"checkin_count": 17,
|
||||
"position_count": 42,
|
||||
"event": {
|
||||
"name": "Demo Converence",
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"name": "T-Shirt",
|
||||
"id": 1,
|
||||
"checkin_count": 1,
|
||||
"admission": False,
|
||||
"position_count": 1,
|
||||
"variations": [
|
||||
{
|
||||
"value": "Red",
|
||||
"id": 1,
|
||||
"checkin_count": 1,
|
||||
"position_count": 12
|
||||
},
|
||||
{
|
||||
"value": "Blue",
|
||||
"id": 2,
|
||||
"checkin_count": 4,
|
||||
"position_count": 8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Ticket",
|
||||
"id": 2,
|
||||
"checkin_count": 15,
|
||||
"admission": True,
|
||||
"position_count": 22,
|
||||
"variations": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param id: The ``id`` field of the check-in list to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
||||
|
||||
Creates a new check-in list.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from django.db.models import F, Max, OuterRef, Prefetch, Subquery
|
||||
from django.db.models import Count, F, Max, OuterRef, Prefetch, Subquery
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import detail_route
|
||||
from rest_framework.response import Response
|
||||
|
||||
from pretix.api.serializers.checkin import CheckinListSerializer
|
||||
from pretix.api.serializers.order import OrderPositionSerializer
|
||||
@@ -66,6 +68,74 @@ class CheckinListViewSet(viewsets.ModelViewSet):
|
||||
)
|
||||
super().perform_destroy(instance)
|
||||
|
||||
@detail_route(methods=['GET'])
|
||||
def status(self, *args, **kwargs):
|
||||
clist = self.get_object()
|
||||
cqs = Checkin.objects.filter(
|
||||
position__order__event=clist.event,
|
||||
position__order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
list=clist
|
||||
)
|
||||
pqs = OrderPosition.objects.filter(
|
||||
order__event=clist.event,
|
||||
order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
subevent=clist.subevent,
|
||||
)
|
||||
if not clist.all_products:
|
||||
pqs = pqs.filter(item__in=clist.limit_products.values_list('id', flat=True))
|
||||
|
||||
ev = clist.subevent or clist.event
|
||||
response = {
|
||||
'event': {
|
||||
'name': str(ev.name),
|
||||
},
|
||||
'checkin_count': cqs.count(),
|
||||
'position_count': pqs.count()
|
||||
}
|
||||
|
||||
op_by_item = {
|
||||
p['item']: p['cnt']
|
||||
for p in pqs.order_by().values('item').annotate(cnt=Count('id'))
|
||||
}
|
||||
op_by_variation = {
|
||||
p['variation']: p['cnt']
|
||||
for p in pqs.order_by().values('variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_item = {
|
||||
p['position__item']: p['cnt']
|
||||
for p in cqs.order_by().values('position__item').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_variation = {
|
||||
p['position__variation']: p['cnt']
|
||||
for p in cqs.order_by().values('position__variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
|
||||
if not clist.all_products:
|
||||
items = clist.limit_products
|
||||
else:
|
||||
items = clist.event.items
|
||||
|
||||
response['items'] = []
|
||||
for item in items.order_by('category__position', 'position', 'pk').prefetch_related('variations'):
|
||||
i = {
|
||||
'id': item.pk,
|
||||
'name': str(item),
|
||||
'admission': item.admission,
|
||||
'checkin_count': c_by_item.get(item.pk, 0),
|
||||
'position_count': op_by_item.get(item.pk, 0),
|
||||
'variations': []
|
||||
}
|
||||
for var in item.variations.all():
|
||||
i['variations'].append({
|
||||
'id': var.pk,
|
||||
'value': str(var),
|
||||
'checkin_count': c_by_variation.get(var.pk, 0),
|
||||
'position_count': op_by_variation.get(var.pk, 0),
|
||||
})
|
||||
response['items'].append(i)
|
||||
|
||||
return Response(response)
|
||||
|
||||
|
||||
class CheckinOrderPositionFilter(OrderPositionFilter):
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from django_countries.fields import Country
|
||||
from pytz import UTC
|
||||
|
||||
from pretix.base.models import (
|
||||
CheckinList, InvoiceAddress, Order, OrderPosition,
|
||||
Checkin, CheckinList, InvoiceAddress, Order, OrderPosition,
|
||||
)
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ TEST_ORDERPOSITION1_RES = {
|
||||
"subevent": None
|
||||
}
|
||||
|
||||
|
||||
TEST_ORDERPOSITION2_RES = {
|
||||
"id": 2,
|
||||
"order": "FOO",
|
||||
@@ -313,14 +312,16 @@ def test_list_all_items_positions(token_client, organizer, event, clist, clist_a
|
||||
assert [p2] == resp.data['results']
|
||||
|
||||
# Order by checkin
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=-last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=-last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
assert [p1, p2] == resp.data['results']
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
assert [p2, p1] == resp.data['results']
|
||||
|
||||
@@ -333,9 +334,10 @@ def test_list_all_items_positions(token_client, organizer, event, clist, clist_a
|
||||
'datetime': c.datetime.isoformat().replace('+00:00', 'Z')
|
||||
}
|
||||
]
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=-last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?ordering=-last_checked_in'.format(
|
||||
organizer.slug, event.slug, clist_all.pk
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
assert [p2, p1] == resp.data['results']
|
||||
|
||||
@@ -387,3 +389,50 @@ def test_list_limited_items_position_detail(token_client, organizer, event, clis
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
assert p1 == resp.data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_status(token_client, organizer, event, clist_all, item, other_item, order):
|
||||
op = order.positions.first()
|
||||
var1 = item.variations.create(value="XS")
|
||||
var2 = item.variations.create(value="S")
|
||||
op.variation = var1
|
||||
op.save()
|
||||
Checkin.objects.create(position=op, list=clist_all)
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/status/'.format(
|
||||
organizer.slug, event.slug, clist_all.pk,
|
||||
))
|
||||
assert resp.status_code == 200
|
||||
assert resp.data['checkin_count'] == 1
|
||||
assert resp.data['position_count'] == 2
|
||||
assert resp.data['items'] == [
|
||||
{
|
||||
'name': str(item.name),
|
||||
'id': item.pk,
|
||||
'checkin_count': 1,
|
||||
'admission': False,
|
||||
'position_count': 1,
|
||||
'variations': [
|
||||
{
|
||||
'id': var1.pk,
|
||||
'value': 'XS',
|
||||
'checkin_count': 1,
|
||||
'position_count': 1,
|
||||
},
|
||||
{
|
||||
'id': var2.pk,
|
||||
'value': 'S',
|
||||
'checkin_count': 0,
|
||||
'position_count': 0,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': other_item.name,
|
||||
'id': other_item.pk,
|
||||
'checkin_count': 0,
|
||||
'admission': False,
|
||||
'position_count': 1,
|
||||
'variations': []
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user