API: Add status view to checkin list resource

This commit is contained in:
Raphael Michel
2018-04-24 19:08:15 +02:00
parent bee7314dd7
commit b41c536865
3 changed files with 201 additions and 12 deletions

View File

@@ -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.

View File

@@ -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):

View File

@@ -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': []
}
]