0001-api-return-only-agenda-with-open-events-44294.patch
chrono/agendas/models.py | ||
---|---|---|
430 | 430 |
for weektime_interval in IntervalSet.simple(*time_period_interval) - closed_hours_by_days: |
431 | 431 |
yield SharedTimePeriod.from_weektime_interval(weektime_interval, desks=desks) |
432 | 432 | |
433 |
def get_open_events(self): |
|
433 |
def get_open_events(self, prefetched_queryset=False):
|
|
434 | 434 |
assert self.kind == 'events' |
435 | 435 | |
436 |
entries = self.event_set.all() |
|
437 |
entries = self.event_set.filter(cancelled=False) |
|
438 |
# we never want to allow booking for past events. |
|
439 |
entries = entries.filter(start_datetime__gte=localtime(now())) |
|
440 |
# exclude non published events |
|
441 |
entries = entries.filter( |
|
442 |
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()) |
|
443 |
) |
|
444 |
if self.minimal_booking_delay: |
|
436 |
if prefetched_queryset: |
|
437 |
entries = self.prefetched_events |
|
438 |
else: |
|
439 |
entries = self.event_set.filter(cancelled=False) |
|
440 |
# we never want to allow booking for past events. |
|
441 |
entries = entries.filter(start_datetime__gte=localtime(now())) |
|
442 |
# exclude non published events |
|
445 | 443 |
entries = entries.filter( |
446 |
start_datetime__gte=localtime( |
|
447 |
now() + datetime.timedelta(days=self.minimal_booking_delay) |
|
448 |
).replace(hour=0, minute=0) |
|
444 |
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()) |
|
449 | 445 |
) |
446 | ||
447 |
if self.minimal_booking_delay: |
|
448 |
min_start = localtime(now() + datetime.timedelta(days=self.minimal_booking_delay)).replace( |
|
449 |
hour=0, minute=0 |
|
450 |
) |
|
451 |
if prefetched_queryset: |
|
452 |
entries = [e for e in entries if e.start_datetime >= min_start] |
|
453 |
else: |
|
454 |
entries = entries.filter(start_datetime__gte=min_start) |
|
450 | 455 |
if self.maximal_booking_delay: |
451 |
entries = entries.filter( |
|
452 |
start_datetime__lt=localtime( |
|
453 |
now() + datetime.timedelta(days=self.maximal_booking_delay) |
|
454 |
).replace(hour=0, minute=0) |
|
456 |
max_start = localtime(now() + datetime.timedelta(days=self.maximal_booking_delay)).replace( |
|
457 |
hour=0, minute=0 |
|
455 | 458 |
) |
459 |
if prefetched_queryset: |
|
460 |
entries = [e for e in entries if e.start_datetime < max_start] |
|
461 |
else: |
|
462 |
entries = entries.filter(start_datetime__lt=max_start) |
|
456 | 463 |
return entries |
457 | 464 | |
458 | 465 |
chrono/api/views.py | ||
---|---|---|
21 | 21 | |
22 | 22 | |
23 | 23 |
from django.db import transaction |
24 |
from django.db.models import Prefetch, Q |
|
24 | 25 |
from django.http import Http404, HttpResponse |
25 | 26 |
from django.shortcuts import get_object_or_404 |
26 | 27 |
from django.urls import reverse |
... | ... | |
352 | 353 | |
353 | 354 |
def get(self, request, format=None): |
354 | 355 |
agendas_queryset = Agenda.objects.all().prefetch_related('resources').order_by('label') |
356 | ||
355 | 357 |
if 'q' in request.GET: |
356 | 358 |
if not request.GET['q']: |
357 | 359 |
return Response({'data': []}) |
358 | 360 |
agendas_queryset = agendas_queryset.filter(slug__icontains=request.GET['q']) |
359 |
agendas = [get_agenda_detail(request, agenda) for agenda in agendas_queryset] |
|
361 | ||
362 |
if 'with_open_events' in request.GET: |
|
363 |
# return only events agenda |
|
364 |
event_queryset = Event.objects.filter( |
|
365 |
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()), |
|
366 |
cancelled=False, |
|
367 |
start_datetime__gte=localtime(now()), |
|
368 |
).order_by() |
|
369 |
agendas_queryset = agendas_queryset.filter(kind='events').prefetch_related( |
|
370 |
Prefetch('event_set', queryset=event_queryset, to_attr='prefetched_events',) |
|
371 |
) |
|
372 | ||
373 |
agendas = [] |
|
374 |
for agenda in agendas_queryset: |
|
375 |
if 'with_open_events' in request.GET and not any( |
|
376 |
not e.full for e in agenda.get_open_events(prefetched_queryset=True) |
|
377 |
): |
|
378 |
# exclude agendas without open events |
|
379 |
continue |
|
380 |
agendas.append(get_agenda_detail(request, agenda)) |
|
381 | ||
360 | 382 |
return Response({'data': agendas}) |
361 | 383 | |
362 | 384 |
tests/test_api.py | ||
---|---|---|
129 | 129 | |
130 | 130 | |
131 | 131 |
def test_agendas_api(app): |
132 |
Agenda.objects.create(label='Foo bar') |
|
133 |
Agenda.objects.create(label='Foo bar 2') |
|
132 |
event_agenda = Agenda.objects.create(label='Foo bar')
|
|
133 |
event_agenda2 = Agenda.objects.create(label='Foo bar 2')
|
|
134 | 134 |
meetings_agenda1 = Agenda.objects.create(label='Foo bar Meeting', kind='meetings') |
135 | 135 |
Agenda.objects.create(label='Foo bar Meeting 2', kind='meetings') |
136 | 136 |
resource1 = Resource.objects.create(label='Resource 1', description='Foo bar Resource 1') |
... | ... | |
228 | 228 |
resp = app.get('/api/agenda/', params={'q': 'MEET'}) |
229 | 229 |
assert len(ctx.captured_queries) == 2 |
230 | 230 | |
231 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
232 |
assert len(resp.json['data']) == 0 |
|
233 | ||
234 |
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0) |
|
235 |
first_date += datetime.timedelta(days=1) |
|
236 |
event1 = Event.objects.create( |
|
237 |
start_datetime=(now() + datetime.timedelta(days=5)).replace(hour=10, minute=0), |
|
238 |
places=20, |
|
239 |
agenda=event_agenda, |
|
240 |
) |
|
241 |
event2 = Event.objects.create( |
|
242 |
start_datetime=(now() + datetime.timedelta(days=10)).replace(hour=10, minute=0), |
|
243 |
places=20, |
|
244 |
agenda=event_agenda, |
|
245 |
) |
|
246 |
event3 = Event.objects.create( |
|
247 |
start_datetime=(now() + datetime.timedelta(days=15)).replace(hour=10, minute=0), |
|
248 |
places=20, |
|
249 |
agenda=event_agenda, |
|
250 |
) |
|
251 | ||
252 |
# all events are free |
|
253 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
254 |
assert len(resp.json['data']) == 1 |
|
255 | ||
256 |
# one event is full |
|
257 |
Event.objects.filter(pk=event1.pk).update(full=True) |
|
258 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
259 |
assert len(resp.json['data']) == 1 |
|
260 | ||
261 |
# all events are full |
|
262 |
Event.objects.update(full=True) |
|
263 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
264 |
assert len(resp.json['data']) == 0 |
|
265 | ||
266 |
# event1 is not full but too soon |
|
267 |
Event.objects.filter(pk=event1.pk).update(full=False) |
|
268 |
event_agenda.minimal_booking_delay = 10 |
|
269 |
event_agenda.save() |
|
270 |
assert list(event_agenda.get_open_events()) == [event2, event3] |
|
271 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
272 |
assert len(resp.json['data']) == 0 |
|
273 | ||
274 |
# event3 is not full but too late |
|
275 |
Event.objects.filter(pk=event3.pk).update(full=False) |
|
276 |
event_agenda.maximal_booking_delay = 12 |
|
277 |
event_agenda.save() |
|
278 |
assert list(event_agenda.get_open_events()) == [event2] |
|
279 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
280 |
assert len(resp.json['data']) == 0 |
|
281 | ||
282 |
# events are not full but not published |
|
283 |
Event.objects.update(full=False) |
|
284 |
event_agenda.event_set.update(publication_date=now().date() + datetime.timedelta(days=20)) |
|
285 |
assert list(event_agenda.get_open_events()) == [] |
|
286 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
287 |
assert len(resp.json['data']) == 0 |
|
288 | ||
289 |
with CaptureQueriesContext(connection) as ctx: |
|
290 |
resp = app.get('/api/agenda/', params={'with_open_events': ''}) |
|
291 |
assert len(ctx.captured_queries) == 3 |
|
292 | ||
231 | 293 | |
232 | 294 |
def test_agendas_meetingtypes_api(app, some_data, meetings_agenda): |
233 | 295 |
resp = app.get('/api/agenda/%s/meetings/' % meetings_agenda.slug) |
234 |
- |