0002-api-get-event-status-and-fillslot-with-event-pk-or-s.patch
chrono/api/urls.py | ||
---|---|---|
23 | 23 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/$', views.agenda_detail), |
24 | 24 | |
25 | 25 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/datetimes/$', views.datetimes, name='api-agenda-datetimes'), |
26 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/fillslot/(?P<event_pk>[\w:-]+)/$',
|
|
26 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/fillslot/(?P<event_identifier>[\w:-]+)/$',
|
|
27 | 27 |
views.fillslot, name='api-fillslot'), |
28 | 28 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/fillslots/$', |
29 | 29 |
views.fillslots, name='api-agenda-fillslots'), |
30 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/status/(?P<event_pk>\d+)/$', views.slot_status,
|
|
30 |
url(r'agenda/(?P<agenda_identifier>[\w-]+)/status/(?P<event_identifier>[\w-]+)/$', views.slot_status,
|
|
31 | 31 |
name='api-event-status'), |
32 | 32 | |
33 | 33 |
url(r'agenda/meetings/(?P<meeting_identifier>[\w-]+)/datetimes/$', |
chrono/api/views.py | ||
---|---|---|
185 | 185 |
datetime.datetime.combine(parse_date(request.GET['date_end']), datetime.time(0, 0)))) |
186 | 186 | |
187 | 187 |
response = {'data': [{'id': x.id, |
188 |
'slug': x.slug, |
|
188 | 189 |
'text': force_text(x), |
189 | 190 |
'datetime': format_response_datetime(x.start_datetime), |
190 | 191 |
'description': x.description, |
... | ... | |
194 | 195 |
reverse('api-fillslot', |
195 | 196 |
kwargs={ |
196 | 197 |
'agenda_identifier': agenda.slug, |
197 |
'event_pk': x.id,
|
|
198 |
'event_identifier': x.slug,
|
|
198 | 199 |
})), |
199 | 200 |
'status_url': request.build_absolute_uri( |
200 | 201 |
reverse('api-event-status', |
201 | 202 |
kwargs={ |
202 | 203 |
'agenda_identifier': agenda.slug, |
203 |
'event_pk': x.id,
|
|
204 |
'event_identifier': x.slug,
|
|
204 | 205 |
})) |
205 | 206 |
}, |
206 | 207 |
} for x in entries]} |
... | ... | |
240 | 241 | |
241 | 242 |
# create fillslot API URL as a template, to avoid expensive calls |
242 | 243 |
# to request.build_absolute_uri() |
243 |
fake_event_pk = '__event_id__'
|
|
244 |
fake_event_identifier = '__event_identifier__'
|
|
244 | 245 |
fillslot_url = request.build_absolute_uri( |
245 | 246 |
reverse('api-fillslot', |
246 | 247 |
kwargs={ |
247 | 248 |
'agenda_identifier': agenda.slug, |
248 |
'event_pk': fake_event_pk,
|
|
249 |
'event_identifier': fake_event_identifier,
|
|
249 | 250 |
})) |
250 | 251 | |
251 | 252 |
response = {'data': [{'id': x.id, |
... | ... | |
253 | 254 |
'text': force_text(x), |
254 | 255 |
'disabled': bool(x.full), |
255 | 256 |
'api': { |
256 |
'fillslot_url': fillslot_url.replace(fake_event_pk, str(x.id)),
|
|
257 |
'fillslot_url': fillslot_url.replace(fake_event_identifier, str(x.id)),
|
|
257 | 258 |
}, |
258 | 259 |
} for x in slots]} |
259 | 260 |
return Response(response) |
... | ... | |
333 | 334 |
permission_classes = (permissions.IsAuthenticated,) |
334 | 335 |
serializer_class = SlotsSerializer |
335 | 336 | |
336 |
def post(self, request, agenda_identifier=None, event_pk=None, format=None):
|
|
337 |
def post(self, request, agenda_identifier=None, event_identifier=None, format=None):
|
|
337 | 338 |
return self.fillslot(request=request, agenda_identifier=agenda_identifier, |
338 | 339 |
format=format) |
339 | 340 | |
... | ... | |
467 | 468 |
full=False, places=1, |
468 | 469 |
desk=available_desk)) |
469 | 470 |
else: |
470 |
events = Event.objects.filter(id__in=slots).order_by('start_datetime') |
|
471 |
try: |
|
472 |
events = Event.objects.filter(id__in=[int(s) for s in slots]).order_by('start_datetime') |
|
473 |
except ValueError: |
|
474 |
events = Event.objects.filter(slug__in=slots).order_by('start_datetime') |
|
471 | 475 | |
472 | 476 |
# search free places. Switch to waiting list if necessary. |
473 | 477 |
in_waiting_list = False |
... | ... | |
538 | 542 |
class Fillslot(Fillslots): |
539 | 543 |
serializer_class = SlotSerializer |
540 | 544 | |
541 |
def post(self, request, agenda_identifier=None, event_pk=None, format=None):
|
|
545 |
def post(self, request, agenda_identifier=None, event_identifier=None, format=None):
|
|
542 | 546 |
return self.fillslot(request=request, |
543 | 547 |
agenda_identifier=agenda_identifier, |
544 |
slots=[event_pk], # fill a "list on one slot"
|
|
548 |
slots=[event_identifier], # fill a "list on one slot"
|
|
545 | 549 |
format=format) |
546 | 550 | |
547 | 551 |
fillslot = Fillslot.as_view() |
... | ... | |
610 | 614 |
class SlotStatus(APIView): |
611 | 615 |
permission_classes = (permissions.IsAuthenticated,) |
612 | 616 | |
613 |
def get(self, request, agenda_identifier=None, event_pk=None, format=None): |
|
614 |
event = get_object_or_404(Event, id=event_pk) |
|
617 |
def get_object(self, event_identifier): |
|
618 |
try: |
|
619 |
return Event.objects.get(slug=event_identifier) |
|
620 |
except Event.DoesNotExist: |
|
621 |
try: |
|
622 |
# legacy access by event id |
|
623 |
return Event.objects.get(pk=int(event_identifier)) |
|
624 |
except (ValueError, Event.DoesNotExist): |
|
625 |
raise Http404() |
|
626 | ||
627 |
def get(self, request, agenda_identifier=None, event_identifier=None, format=None): |
|
628 |
event = self.get_object(event_identifier) |
|
615 | 629 |
response = { |
616 | 630 |
'err': 0, |
617 | 631 |
'places': { |
... | ... | |
626 | 640 |
response['places']['waiting_list_available'] = (event.waiting_list_places - event.waiting_list) |
627 | 641 |
return Response(response) |
628 | 642 | |
643 | ||
629 | 644 |
slot_status = SlotStatus.as_view() |
630 | 645 | |
631 | 646 |
tests/test_api.py | ||
---|---|---|
225 | 225 |
agenda = Agenda.objects.get(label=u'Foo bar2') |
226 | 226 |
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) |
227 | 227 |
for datum in resp.json['data']: |
228 |
assert urlparse.urlparse(datum['api']['status_url']).path == '/api/agenda/%s/status/%s/' % (agenda.slug, datum['id'])
|
|
228 |
assert urlparse.urlparse(datum['api']['status_url']).path == '/api/agenda/%s/status/%s/' % (agenda.slug, datum['slug'])
|
|
229 | 229 | |
230 | 230 |
def test_datetimes_api_meetings_agenda(app, meetings_agenda): |
231 | 231 |
meeting_type = MeetingType.objects.get(agenda=meetings_agenda) |
... | ... | |
333 | 333 |
for agenda_key in (agenda.slug, agenda.id): # acces datetimes via agenda slug or id (legacy) |
334 | 334 |
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda_key) |
335 | 335 |
event_fillslot_url = [x for x in resp_datetimes.json['data'] if x['id'] == event.id][0]['api']['fillslot_url'] |
336 |
assert urlparse.urlparse(event_fillslot_url).path == '/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id)
|
|
336 |
assert urlparse.urlparse(event_fillslot_url).path == '/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug)
|
|
337 | 337 | |
338 | 338 |
app.authorization = ('Basic', ('john.doe', 'password')) |
339 | 339 |
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id)) |
... | ... | |
346 | 346 |
assert urlparse.urlparse(resp.json['api']['ics_url']).netloc |
347 | 347 |
assert Booking.objects.count() == 1 |
348 | 348 | |
349 |
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id)) |
|
349 |
# access by slug |
|
350 |
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.slug)) |
|
350 | 351 |
assert Booking.objects.count() == 2 |
351 | 352 |
assert Booking.objects.filter(event__agenda=agenda).count() == 2 |
352 | 353 | |
... | ... | |
467 | 468 |
def test_booking_api_fillslots(app, some_data, user): |
468 | 469 |
agenda = Agenda.objects.filter(label=u'Foo bar')[0] |
469 | 470 |
events_ids = [x.id for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()] |
471 |
events_slugs = [x.slug for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()] |
|
470 | 472 |
assert len(events_ids) == 3 |
471 | 473 |
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0] # first event |
472 | 474 | |
... | ... | |
495 | 497 |
assert bookings[1].primary_booking.id == bookings[0].id == primary_booking_id |
496 | 498 |
assert bookings[2].primary_booking.id == bookings[0].id == primary_booking_id |
497 | 499 | |
498 |
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': events_ids}) |
|
500 |
# access by slug |
|
501 |
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': events_slugs}) |
|
499 | 502 |
primary_booking_id_2 = resp.json['booking_id'] |
500 | 503 |
assert Booking.objects.count() == 6 |
501 | 504 |
assert Booking.objects.filter(event__agenda=agenda).count() == 6 |
... | ... | |
915 | 918 |
assert resp.json['places']['waiting_list_available'] == 4 |
916 | 919 |
assert resp.json['places']['waiting_list_reserved'] == 1 |
917 | 920 | |
921 |
# access by slug |
|
922 |
resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, event.slug)) |
|
923 |
# not found event |
|
924 |
resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, 'unknown'), status=404) |
|
925 | ||
926 | ||
918 | 927 |
def test_waiting_list_datetimes(app, some_data, user): |
919 | 928 |
agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id |
920 | 929 |
event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0] |
... | ... | |
1343 | 1352 |
booking_url = event_data['api']['fillslot_url'] |
1344 | 1353 |
with CaptureQueriesContext(connection) as ctx: |
1345 | 1354 |
app.post(booking_url) |
1346 |
assert len(ctx.captured_queries) == queries_count_fillslot1 |
|
1355 |
# 2 + idx: because of slug unicity |
|
1356 |
assert len(ctx.captured_queries) == queries_count_fillslot1 + 2 + idx |
|
1347 | 1357 | |
1348 | 1358 |
with CaptureQueriesContext(connection) as ctx: |
1349 | 1359 |
app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id) |
1350 |
- |