32 |
32 |
from django.utils.encoding import force_text
|
33 |
33 |
from django.utils.formats import date_format
|
34 |
34 |
from django.utils.timezone import localtime, make_aware, now
|
35 |
|
from django.utils.translation import gettext, gettext_noop
|
|
35 |
from django.utils.translation import gettext
|
|
36 |
from django.utils.translation import gettext_noop as N_
|
36 |
37 |
from django.utils.translation import ugettext_lazy as _
|
37 |
38 |
from django_filters import rest_framework as filters
|
38 |
|
from rest_framework import permissions, status
|
|
39 |
from rest_framework import permissions
|
39 |
40 |
from rest_framework.exceptions import ValidationError
|
40 |
41 |
from rest_framework.generics import ListAPIView
|
41 |
42 |
from rest_framework.views import APIView
|
... | ... | |
51 |
52 |
TimePeriodException,
|
52 |
53 |
)
|
53 |
54 |
from chrono.api import serializers
|
54 |
|
from chrono.api.utils import APIError, Response
|
|
55 |
from chrono.api.utils import APIError, APIErrorBadRequest, Response
|
55 |
56 |
from chrono.interval import IntervalSet
|
56 |
57 |
from chrono.utils.publik_urls import translate_to_publik_url
|
57 |
58 |
|
... | ... | |
570 |
571 |
try:
|
571 |
572 |
start_datetime = make_aware(datetime.datetime.strptime(datetime_str, '%Y-%m-%d-%H%M'))
|
572 |
573 |
except ValueError:
|
573 |
|
raise APIError(
|
574 |
|
_('bad datetime format: %s') % datetime_str,
|
575 |
|
err_class='bad datetime format: %s' % datetime_str,
|
576 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
577 |
|
)
|
|
574 |
raise APIErrorBadRequest(N_('bad datetime format: %s') % datetime_str)
|
578 |
575 |
try:
|
579 |
576 |
event = agenda.event_set.get(slug=event_slug)
|
580 |
577 |
except Event.DoesNotExist:
|
581 |
|
raise APIError(
|
582 |
|
_('unknown recurring event slug: %s') % event_slug,
|
583 |
|
err_class='unknown recurring event slug: %s' % event_slug,
|
584 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
585 |
|
)
|
|
578 |
raise APIErrorBadRequest(N_('unknown recurring event slug: %s') % event_slug)
|
586 |
579 |
try:
|
587 |
580 |
return event.get_or_create_event_recurrence(start_datetime)
|
588 |
581 |
except ValueError:
|
589 |
|
raise APIError(
|
590 |
|
_('invalid datetime for event %s') % event_identifier,
|
591 |
|
err_class='invalid datetime for event %s' % event_identifier,
|
592 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
593 |
|
)
|
|
582 |
raise APIErrorBadRequest(N_('invalid datetime for event %s') % event_identifier)
|
594 |
583 |
|
595 |
584 |
|
596 |
585 |
def get_events_from_slots(slots, request, agenda, payload):
|
... | ... | |
616 |
605 |
for event in events:
|
617 |
606 |
if event.start_datetime >= now():
|
618 |
607 |
if not book_future or not event.in_bookable_period(bypass_delays=bypass_delays):
|
619 |
|
raise APIError(_('event %s is not bookable') % event.slug, err_class='event not bookable')
|
|
608 |
raise APIError(N_('event %s is not bookable') % event.slug, err_class='event not bookable')
|
620 |
609 |
else:
|
621 |
610 |
if not book_past:
|
622 |
|
raise APIError(_('event %s is not bookable') % event.slug, err_class='event not bookable')
|
|
611 |
raise APIError(N_('event %s is not bookable') % event.slug, err_class='event not bookable')
|
623 |
612 |
if event.cancelled:
|
624 |
|
raise APIError(_('event %s is cancelled') % event.slug, err_class='event is cancelled')
|
|
613 |
raise APIError(N_('event %s is cancelled') % event.slug, err_class='event is cancelled')
|
625 |
614 |
if exclude_user and user_external_id:
|
626 |
615 |
if event.booking_set.filter(user_external_id=user_external_id).exists():
|
627 |
616 |
raise APIError(
|
628 |
|
_('event %s is already booked by user') % event.slug,
|
|
617 |
N_('event %s is already booked by user') % event.slug,
|
629 |
618 |
err_class='event is already booked by user',
|
630 |
619 |
)
|
631 |
620 |
if event.recurrence_days:
|
632 |
621 |
raise APIError(
|
633 |
|
_('event %s is recurrent, direct booking is forbidden') % event.slug,
|
|
622 |
N_('event %s is recurrent, direct booking is forbidden') % event.slug,
|
634 |
623 |
err_class='event is recurrent',
|
635 |
624 |
)
|
636 |
625 |
|
637 |
626 |
if slots and not events.exists():
|
638 |
|
raise APIError(
|
639 |
|
_('unknown event identifiers or slugs'),
|
640 |
|
err_class='unknown event identifiers or slugs',
|
641 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
642 |
|
)
|
|
627 |
raise APIErrorBadRequest(N_('unknown event identifiers or slugs'))
|
643 |
628 |
return events
|
644 |
629 |
|
645 |
630 |
|
... | ... | |
656 |
641 |
if len(objects) != len(slugs):
|
657 |
642 |
unknown_slugs = sorted(slugs - {obj.slug for obj in objects})
|
658 |
643 |
unknown_slugs = ', '.join(unknown_slugs)
|
659 |
|
raise APIError(
|
660 |
|
_('invalid slugs: %s' % unknown_slugs),
|
661 |
|
err_class='invalid slugs: %s' % unknown_slugs,
|
662 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
663 |
|
)
|
|
644 |
raise APIErrorBadRequest(N_('invalid slugs: %s' % unknown_slugs))
|
664 |
645 |
return objects
|
665 |
646 |
|
666 |
647 |
|
667 |
648 |
def get_start_and_end_datetime_from_request(request):
|
668 |
649 |
serializer = serializers.DateRangeSerializer(data=request.query_params)
|
669 |
650 |
if not serializer.is_valid():
|
670 |
|
raise APIError(
|
671 |
|
_('invalid payload'),
|
672 |
|
err_class='invalid payload',
|
673 |
|
errors=serializer.errors,
|
674 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
675 |
|
)
|
|
651 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
676 |
652 |
|
677 |
653 |
return serializer.validated_data.get('date_start'), serializer.validated_data.get('date_end')
|
678 |
654 |
|
... | ... | |
680 |
656 |
def get_agendas_from_request(request):
|
681 |
657 |
serializer = serializers.AgendaSlugsSerializer(data=request.query_params)
|
682 |
658 |
if not serializer.is_valid():
|
683 |
|
raise APIError(
|
684 |
|
_('invalid payload'),
|
685 |
|
err_class='invalid payload',
|
686 |
|
errors=serializer.errors,
|
687 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
688 |
|
)
|
|
659 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
689 |
660 |
|
690 |
661 |
return serializer.validated_data.get('agendas')
|
691 |
662 |
|
... | ... | |
757 |
728 |
def post(self, request, format=None):
|
758 |
729 |
serializer = self.serializer_class(data=request.data)
|
759 |
730 |
if not serializer.is_valid():
|
760 |
|
raise APIError(
|
761 |
|
_('invalid payload'),
|
762 |
|
err_class='invalid payload',
|
763 |
|
errors=serializer.errors,
|
764 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
765 |
|
)
|
|
731 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
766 |
732 |
agenda = serializer.save()
|
767 |
733 |
return Response({'err': 0, 'data': [get_agenda_detail(request, agenda)]})
|
768 |
734 |
|
... | ... | |
803 |
769 |
|
804 |
770 |
serializer = self.serializer_class(data=request.query_params)
|
805 |
771 |
if not serializer.is_valid():
|
806 |
|
raise APIError(
|
807 |
|
_('invalid payload'),
|
808 |
|
err_class='invalid payload',
|
809 |
|
errors=serializer.errors,
|
810 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
811 |
|
)
|
|
772 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
812 |
773 |
payload = serializer.validated_data
|
813 |
774 |
|
814 |
775 |
user_external_id = payload.get('user_external_id') or payload.get('exclude_user_external_id')
|
... | ... | |
876 |
837 |
def get(self, request):
|
877 |
838 |
serializer = self.serializer_class(data=request.query_params)
|
878 |
839 |
if not serializer.is_valid():
|
879 |
|
raise APIError(
|
880 |
|
_('invalid payload'),
|
881 |
|
err_class='invalid payload',
|
882 |
|
errors=serializer.errors,
|
883 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
884 |
|
)
|
|
840 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
885 |
841 |
payload = serializer.validated_data
|
886 |
842 |
|
887 |
843 |
agenda_slugs = payload['agendas']
|
... | ... | |
969 |
925 |
and excluded_user_external_id
|
970 |
926 |
and booked_user_external_id != excluded_user_external_id
|
971 |
927 |
):
|
972 |
|
raise APIError(
|
973 |
|
_('user_external_id and exclude_user_external_id have different values'),
|
974 |
|
err_class='user_external_id and exclude_user_external_id have different values',
|
975 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
|
928 |
raise APIErrorBadRequest(
|
|
929 |
N_('user_external_id and exclude_user_external_id have different values')
|
976 |
930 |
)
|
977 |
931 |
|
978 |
932 |
# Generate an unique slot for each possible meeting [start_datetime,
|
... | ... | |
1244 |
1198 |
)
|
1245 |
1199 |
if known_body_params:
|
1246 |
1200 |
params = ', '.join(sorted(list(known_body_params)))
|
1247 |
|
raise APIError(
|
1248 |
|
_('parameters "%s" must be included in request body, not query') % params,
|
1249 |
|
err_class='parameters "%s" must be included in request body, not query' % params,
|
1250 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
|
1201 |
raise APIErrorBadRequest(
|
|
1202 |
N_('parameters "%s" must be included in request body, not query') % params
|
1251 |
1203 |
)
|
1252 |
1204 |
|
1253 |
1205 |
serializer = self.serializer_class(data=request.data, partial=True)
|
1254 |
1206 |
if not serializer.is_valid():
|
1255 |
|
raise APIError(
|
1256 |
|
_('invalid payload'),
|
1257 |
|
err_class='invalid payload',
|
1258 |
|
errors=serializer.errors,
|
1259 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1260 |
|
)
|
|
1207 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
1261 |
1208 |
payload = serializer.validated_data
|
1262 |
1209 |
|
1263 |
1210 |
if 'slots' in payload:
|
... | ... | |
1270 |
1217 |
try:
|
1271 |
1218 |
places_count = int(request.query_params['count'])
|
1272 |
1219 |
except ValueError:
|
1273 |
|
raise APIError(
|
1274 |
|
_('invalid value for count (%s)') % request.query_params['count'],
|
1275 |
|
err_class='invalid value for count (%s)' % request.query_params['count'],
|
1276 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1277 |
|
)
|
|
1220 |
raise APIErrorBadRequest(N_('invalid value for count (%s)') % request.query_params['count'])
|
1278 |
1221 |
else:
|
1279 |
1222 |
places_count = 1
|
1280 |
1223 |
|
1281 |
1224 |
if places_count <= 0:
|
1282 |
|
raise APIError(
|
1283 |
|
_('count cannot be less than or equal to zero'),
|
1284 |
|
err_class='count cannot be less than or equal to zero',
|
1285 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1286 |
|
)
|
|
1225 |
raise APIErrorBadRequest(N_('count cannot be less than or equal to zero'))
|
1287 |
1226 |
|
1288 |
1227 |
to_cancel_booking = None
|
1289 |
1228 |
cancel_booking_id = None
|
... | ... | |
1291 |
1230 |
try:
|
1292 |
1231 |
cancel_booking_id = int(payload.get('cancel_booking_id'))
|
1293 |
1232 |
except (ValueError, TypeError):
|
1294 |
|
raise APIError(
|
1295 |
|
_('cancel_booking_id is not an integer'),
|
1296 |
|
err_class='cancel_booking_id is not an integer',
|
1297 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1298 |
|
)
|
|
1233 |
raise APIErrorBadRequest(N_('cancel_booking_id is not an integer'))
|
1299 |
1234 |
|
1300 |
1235 |
if cancel_booking_id is not None:
|
1301 |
1236 |
cancel_error = None
|
1302 |
1237 |
try:
|
1303 |
1238 |
to_cancel_booking = Booking.objects.get(pk=cancel_booking_id)
|
1304 |
1239 |
if to_cancel_booking.cancellation_datetime:
|
1305 |
|
cancel_error = gettext_noop('cancel booking: booking already cancelled')
|
|
1240 |
cancel_error = N_('cancel booking: booking already cancelled')
|
1306 |
1241 |
else:
|
1307 |
1242 |
to_cancel_places_count = (
|
1308 |
1243 |
to_cancel_booking.secondary_booking_set.filter(event=to_cancel_booking.event).count()
|
1309 |
1244 |
+ 1
|
1310 |
1245 |
)
|
1311 |
1246 |
if places_count != to_cancel_places_count:
|
1312 |
|
cancel_error = gettext_noop('cancel booking: count is different')
|
|
1247 |
cancel_error = N_('cancel booking: count is different')
|
1313 |
1248 |
except Booking.DoesNotExist:
|
1314 |
|
cancel_error = gettext_noop('cancel booking: booking does no exist')
|
|
1249 |
cancel_error = N_('cancel booking: booking does no exist')
|
1315 |
1250 |
|
1316 |
1251 |
if cancel_error:
|
1317 |
|
raise APIError(_(cancel_error), err_class=cancel_error)
|
|
1252 |
raise APIError(N_(cancel_error))
|
1318 |
1253 |
|
1319 |
1254 |
extra_data = {}
|
1320 |
1255 |
for k, v in request.data.items():
|
... | ... | |
1335 |
1270 |
try:
|
1336 |
1271 |
meeting_type_id_, datetime_str = slot.split(':')
|
1337 |
1272 |
except ValueError:
|
1338 |
|
raise APIError(
|
1339 |
|
_('invalid slot: %s') % slot,
|
1340 |
|
err_class='invalid slot: %s' % slot,
|
1341 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1342 |
|
)
|
|
1273 |
raise APIErrorBadRequest(N_('invalid slot: %s') % slot)
|
1343 |
1274 |
if meeting_type_id_ != meeting_type_id:
|
1344 |
|
raise APIError(
|
1345 |
|
_('all slots must have the same meeting type id (%s)') % meeting_type_id,
|
1346 |
|
err_class='all slots must have the same meeting type id (%s)' % meeting_type_id,
|
1347 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
|
1275 |
raise APIErrorBadRequest(
|
|
1276 |
N_('all slots must have the same meeting type id (%s)') % meeting_type_id
|
1348 |
1277 |
)
|
1349 |
1278 |
try:
|
1350 |
1279 |
datetimes.add(make_aware(datetime.datetime.strptime(datetime_str, '%Y-%m-%d-%H%M')))
|
1351 |
1280 |
except ValueError:
|
1352 |
|
raise APIError(
|
1353 |
|
_('bad datetime format: %s') % datetime_str,
|
1354 |
|
err_class='bad datetime format: %s' % datetime_str,
|
1355 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1356 |
|
)
|
|
1281 |
raise APIErrorBadRequest(N_('bad datetime format: %s') % datetime_str)
|
1357 |
1282 |
|
1358 |
1283 |
resources = get_resources_from_request(request, agenda)
|
1359 |
1284 |
|
... | ... | |
1365 |
1290 |
# legacy access by id
|
1366 |
1291 |
meeting_type = agenda.get_meetingtype(id_=meeting_type_id)
|
1367 |
1292 |
except (MeetingType.DoesNotExist, ValueError):
|
1368 |
|
raise APIError(
|
1369 |
|
_('invalid meeting type id: %s') % meeting_type_id,
|
1370 |
|
err_class='invalid meeting type id: %s' % meeting_type_id,
|
1371 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1372 |
|
)
|
|
1293 |
raise APIErrorBadRequest(N_('invalid meeting type id: %s') % meeting_type_id)
|
1373 |
1294 |
all_slots = sorted(
|
1374 |
1295 |
get_all_slots(
|
1375 |
1296 |
agenda,
|
... | ... | |
1439 |
1360 |
break
|
1440 |
1361 |
|
1441 |
1362 |
if available_desk is None:
|
1442 |
|
raise APIError(
|
1443 |
|
_('no more desk available'),
|
1444 |
|
err_class='no more desk available',
|
1445 |
|
)
|
|
1363 |
raise APIError(N_('no more desk available'))
|
1446 |
1364 |
|
1447 |
1365 |
# all datetimes are free, book them in order
|
1448 |
1366 |
datetimes = list(datetimes)
|
... | ... | |
1476 |
1394 |
for event in events:
|
1477 |
1395 |
if event.start_datetime > now():
|
1478 |
1396 |
if payload.get('force_waiting_list') and not event.waiting_list_places:
|
1479 |
|
raise APIError(
|
1480 |
|
_('no waiting list'),
|
1481 |
|
err_class='no waiting list',
|
1482 |
|
)
|
|
1397 |
raise APIError(N_('no waiting list'))
|
1483 |
1398 |
|
1484 |
1399 |
if event.waiting_list_places:
|
1485 |
1400 |
if (
|
... | ... | |
1491 |
1406 |
# in the waiting list.
|
1492 |
1407 |
in_waiting_list = True
|
1493 |
1408 |
if (event.booked_waiting_list_places + places_count) > event.waiting_list_places:
|
1494 |
|
raise APIError(
|
1495 |
|
_('sold out'),
|
1496 |
|
err_class='sold out',
|
1497 |
|
)
|
|
1409 |
raise APIError(N_('sold out'))
|
1498 |
1410 |
else:
|
1499 |
1411 |
if (event.booked_places + places_count) > event.places:
|
1500 |
|
raise APIError(
|
1501 |
|
_('sold out'),
|
1502 |
|
err_class='sold out',
|
1503 |
|
)
|
|
1412 |
raise APIError(N_('sold out'))
|
1504 |
1413 |
|
1505 |
1414 |
with transaction.atomic():
|
1506 |
1415 |
if to_cancel_booking:
|
... | ... | |
1618 |
1527 |
context = {'allowed_agenda_slugs': agenda_slugs, 'agendas': Agenda.prefetch_recurring_events(agendas)}
|
1619 |
1528 |
serializer = self.serializer_class(data=request.data, partial=True, context=context)
|
1620 |
1529 |
if not serializer.is_valid():
|
1621 |
|
raise APIError(
|
1622 |
|
_('invalid payload'),
|
1623 |
|
err_class='invalid payload',
|
1624 |
|
errors=serializer.errors,
|
1625 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1626 |
|
)
|
|
1530 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
1627 |
1531 |
payload = serializer.validated_data
|
1628 |
1532 |
user_external_id = payload['user_external_id']
|
1629 |
1533 |
agendas = Agenda.prefetch_events_and_exceptions(agendas, user_external_id=user_external_id)
|
... | ... | |
1700 |
1604 |
data=request.data, partial=True, context=self.serializer_extra_context
|
1701 |
1605 |
)
|
1702 |
1606 |
if not serializer.is_valid():
|
1703 |
|
raise APIError(
|
1704 |
|
_('invalid payload'),
|
1705 |
|
err_class='invalid payload',
|
1706 |
|
errors=serializer.errors,
|
1707 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
1708 |
|
)
|
|
1607 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
1709 |
1608 |
payload = serializer.validated_data
|
1710 |
1609 |
user_external_id = payload['user_external_id']
|
1711 |
1610 |
|
... | ... | |
1723 |
1622 |
full_events = [str(event) for event in events.filter(full=True)]
|
1724 |
1623 |
if full_events:
|
1725 |
1624 |
raise APIError(
|
1726 |
|
_('some events are full: %s') % ', '.join(full_events), err_class='some events are full'
|
|
1625 |
N_('some events are full: %s') % ', '.join(full_events), err_class='some events are full'
|
1727 |
1626 |
)
|
1728 |
1627 |
|
1729 |
1628 |
events = events.annotate(
|
... | ... | |
1827 |
1726 |
|
1828 |
1727 |
def get(self, request, *args, **kwargs):
|
1829 |
1728 |
if not request.GET.get('user_external_id'):
|
1830 |
|
response = {
|
1831 |
|
'err': 1,
|
1832 |
|
'err_class': 'missing param user_external_id',
|
1833 |
|
'err_desc': _('missing param user_external_id'),
|
1834 |
|
}
|
1835 |
|
return Response(response)
|
|
1729 |
raise APIError(N_('missing param user_external_id'))
|
1836 |
1730 |
|
1837 |
1731 |
try:
|
1838 |
1732 |
response = super().get(request, *args, **kwargs)
|
1839 |
1733 |
except ValidationError as e:
|
1840 |
|
return Response(
|
1841 |
|
{
|
1842 |
|
'err': 1,
|
1843 |
|
'err_class': 'invalid payload',
|
1844 |
|
'err_desc': _('invalid payload'),
|
1845 |
|
'errors': e.detail,
|
1846 |
|
},
|
1847 |
|
status=status.HTTP_400_BAD_REQUEST,
|
1848 |
|
)
|
|
1734 |
raise APIErrorBadRequest(N_('invalid payload'), errors=e.detail)
|
1849 |
1735 |
|
1850 |
1736 |
return Response({'err': 0, 'data': response.data})
|
1851 |
1737 |
|
... | ... | |
1866 |
1752 |
|
1867 |
1753 |
def check_booking(self, check_waiting_list=False):
|
1868 |
1754 |
if self.booking.cancellation_datetime:
|
1869 |
|
return Response(
|
1870 |
|
{'err': 1, 'err_class': 'booking is cancelled', 'err_desc': _('booking is cancelled')}
|
1871 |
|
)
|
|
1755 |
raise APIError(N_('booking is cancelled'))
|
1872 |
1756 |
|
1873 |
1757 |
if self.booking.primary_booking is not None:
|
1874 |
|
return Response({'err': 2, 'err_class': 'secondary booking', 'err_desc': _('secondary booking')})
|
|
1758 |
raise APIError(N_('secondary booking'), err=2)
|
1875 |
1759 |
|
1876 |
1760 |
if check_waiting_list and self.booking.in_waiting_list:
|
1877 |
|
response = {
|
1878 |
|
'err': 3,
|
1879 |
|
'err_class': 'booking is in waiting list',
|
1880 |
|
'err_desc': _('booking is in waiting list'),
|
1881 |
|
}
|
1882 |
|
return Response(response)
|
|
1761 |
raise APIError(N_('booking is in waiting list'), err=3)
|
1883 |
1762 |
|
1884 |
1763 |
def get(self, request, *args, **kwargs):
|
1885 |
|
response = self.check_booking()
|
1886 |
|
if response:
|
1887 |
|
return response
|
|
1764 |
self.check_booking()
|
1888 |
1765 |
|
1889 |
1766 |
serializer = self.serializer_class(self.booking)
|
1890 |
1767 |
response = serializer.data
|
... | ... | |
1897 |
1774 |
return Response(response)
|
1898 |
1775 |
|
1899 |
1776 |
def patch(self, request, *args, **kwargs):
|
1900 |
|
response = self.check_booking(check_waiting_list=True)
|
1901 |
|
if response:
|
1902 |
|
return response
|
|
1777 |
self.check_booking(check_waiting_list=True)
|
1903 |
1778 |
|
1904 |
1779 |
serializer = self.serializer_class(self.booking, data=request.data, partial=True)
|
1905 |
1780 |
|
1906 |
1781 |
if not serializer.is_valid():
|
1907 |
|
return Response(
|
1908 |
|
{
|
1909 |
|
'err': 4,
|
1910 |
|
'err_class': 'invalid payload',
|
1911 |
|
'err_desc': _('invalid payload'),
|
1912 |
|
'errors': serializer.errors,
|
1913 |
|
},
|
1914 |
|
status=status.HTTP_400_BAD_REQUEST,
|
1915 |
|
)
|
|
1782 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors, err=4)
|
1916 |
1783 |
|
1917 |
1784 |
if (
|
1918 |
1785 |
self.booking.event.checked
|
1919 |
1786 |
and self.booking.event.agenda.disable_check_update
|
1920 |
1787 |
and ('user_was_present' in request.data or 'user_absence_reason' in request.data)
|
1921 |
1788 |
):
|
1922 |
|
return Response(
|
1923 |
|
{
|
1924 |
|
'err': 5,
|
1925 |
|
'err_class': 'event is marked as checked',
|
1926 |
|
'err_desc': _('event is marked as checked'),
|
1927 |
|
'errors': serializer.errors,
|
1928 |
|
},
|
1929 |
|
status=status.HTTP_400_BAD_REQUEST,
|
1930 |
|
)
|
|
1789 |
raise APIErrorBadRequest(N_('event is marked as checked'), err=5)
|
1931 |
1790 |
|
1932 |
1791 |
if 'extra_data' in serializer.validated_data:
|
1933 |
1792 |
extra_data = self.booking.extra_data or {}
|
... | ... | |
1947 |
1806 |
return Response(response)
|
1948 |
1807 |
|
1949 |
1808 |
def delete(self, request, *args, **kwargs):
|
1950 |
|
response = self.check_booking()
|
1951 |
|
if response:
|
1952 |
|
return response
|
|
1809 |
self.check_booking()
|
1953 |
1810 |
|
1954 |
1811 |
self.booking.cancel()
|
1955 |
1812 |
response = {'err': 0, 'booking_id': self.booking.pk}
|
... | ... | |
1972 |
1829 |
def post(self, request, booking_pk=None, format=None):
|
1973 |
1830 |
booking = get_object_or_404(Booking, id=booking_pk)
|
1974 |
1831 |
if booking.cancellation_datetime:
|
1975 |
|
response = {
|
1976 |
|
'err': 1,
|
1977 |
|
'err_class': 'already cancelled',
|
1978 |
|
'err_desc': _('already cancelled'),
|
1979 |
|
}
|
1980 |
|
return Response(response)
|
|
1832 |
raise APIError(N_('already cancelled'))
|
1981 |
1833 |
if booking.primary_booking is not None:
|
1982 |
|
response = {
|
1983 |
|
'err': 2,
|
1984 |
|
'err_class': 'secondary booking',
|
1985 |
|
'err_desc': _('secondary booking'),
|
1986 |
|
}
|
1987 |
|
return Response(response)
|
|
1834 |
raise APIError(N_('secondary booking'), err=2)
|
1988 |
1835 |
booking.cancel()
|
1989 |
1836 |
response = {'err': 0, 'booking_id': booking.id}
|
1990 |
1837 |
return Response(response)
|
... | ... | |
2007 |
1854 |
def post(self, request, booking_pk=None, format=None):
|
2008 |
1855 |
booking = get_object_or_404(Booking, id=booking_pk, event__agenda__kind='events')
|
2009 |
1856 |
if booking.cancellation_datetime:
|
2010 |
|
response = {
|
2011 |
|
'err': 1,
|
2012 |
|
'err_class': 'booking is cancelled',
|
2013 |
|
'err_desc': _('booking is cancelled'),
|
2014 |
|
}
|
2015 |
|
return Response(response)
|
|
1857 |
raise APIError(N_('booking is cancelled'))
|
2016 |
1858 |
if booking.primary_booking is not None:
|
2017 |
|
response = {
|
2018 |
|
'err': 2,
|
2019 |
|
'err_class': 'secondary booking',
|
2020 |
|
'err_desc': _('secondary booking'),
|
2021 |
|
}
|
2022 |
|
return Response(response)
|
|
1859 |
raise APIError(N_('secondary booking'), err=2)
|
2023 |
1860 |
if not booking.in_waiting_list:
|
2024 |
|
response = {
|
2025 |
|
'err': 3,
|
2026 |
|
'err_class': 'booking is not in waiting list',
|
2027 |
|
'err_desc': _('booking is not in waiting list'),
|
2028 |
|
}
|
2029 |
|
return Response(response)
|
|
1861 |
raise APIError(N_('booking is not in waiting list'), err=3)
|
2030 |
1862 |
booking.accept()
|
2031 |
1863 |
event = booking.event
|
2032 |
1864 |
response = {
|
... | ... | |
2054 |
1886 |
def post(self, request, booking_pk=None, format=None):
|
2055 |
1887 |
booking = get_object_or_404(Booking, pk=booking_pk, event__agenda__kind='events')
|
2056 |
1888 |
if booking.cancellation_datetime:
|
2057 |
|
response = {
|
2058 |
|
'err': 1,
|
2059 |
|
'err_class': 'booking is cancelled',
|
2060 |
|
'err_desc': _('booking is cancelled'),
|
2061 |
|
}
|
2062 |
|
return Response(response)
|
|
1889 |
raise APIError(N_('booking is cancelled'))
|
2063 |
1890 |
if booking.primary_booking is not None:
|
2064 |
|
response = {
|
2065 |
|
'err': 2,
|
2066 |
|
'err_class': 'secondary booking',
|
2067 |
|
'err_desc': _('secondary booking'),
|
2068 |
|
}
|
2069 |
|
return Response(response)
|
|
1891 |
raise APIError(N_('secondary booking'), err=2)
|
2070 |
1892 |
if booking.in_waiting_list:
|
2071 |
|
response = {
|
2072 |
|
'err': 3,
|
2073 |
|
'err_class': 'booking is already in waiting list',
|
2074 |
|
'err_desc': _('booking is already in waiting list'),
|
2075 |
|
}
|
2076 |
|
return Response(response)
|
|
1893 |
raise APIError(N_('booking is already in waiting list'), err=3)
|
2077 |
1894 |
booking.suspend()
|
2078 |
1895 |
response = {'err': 0, 'booking_id': booking.pk}
|
2079 |
1896 |
return Response(response)
|
... | ... | |
2112 |
1929 |
def post(self, request, booking_pk=None, format=None):
|
2113 |
1930 |
serializer = self.serializer_class(data=request.data)
|
2114 |
1931 |
if not serializer.is_valid():
|
2115 |
|
return Response(
|
2116 |
|
{
|
2117 |
|
'err': 1,
|
2118 |
|
'err_class': 'invalid payload',
|
2119 |
|
'err_desc': _('invalid payload'),
|
2120 |
|
'errors': serializer.errors,
|
2121 |
|
},
|
2122 |
|
status=status.HTTP_400_BAD_REQUEST,
|
2123 |
|
)
|
|
1932 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
2124 |
1933 |
payload = serializer.validated_data
|
2125 |
1934 |
|
2126 |
1935 |
booking = get_object_or_404(Booking, pk=booking_pk, event__agenda__kind='events')
|
2127 |
1936 |
event = booking.event
|
2128 |
1937 |
if booking.cancellation_datetime:
|
2129 |
|
response = {
|
2130 |
|
'err': 1,
|
2131 |
|
'err_class': 'booking is cancelled',
|
2132 |
|
'err_desc': _('booking is cancelled'),
|
2133 |
|
}
|
2134 |
|
return Response(response)
|
|
1938 |
raise APIError(N_('booking is cancelled'))
|
2135 |
1939 |
if booking.primary_booking is not None:
|
2136 |
|
response = {
|
2137 |
|
'err': 2,
|
2138 |
|
'err_class': 'secondary booking',
|
2139 |
|
'err_desc': _('secondary booking'),
|
2140 |
|
}
|
2141 |
|
return Response(response)
|
|
1940 |
raise APIError(N_('secondary booking'), err=2)
|
2142 |
1941 |
event_ids = {event.pk}
|
2143 |
1942 |
in_waiting_list = {booking.in_waiting_list}
|
2144 |
1943 |
secondary_bookings = booking.secondary_booking_set.all().order_by('-creation_datetime')
|
... | ... | |
2146 |
1945 |
event_ids.add(secondary.event_id)
|
2147 |
1946 |
in_waiting_list.add(secondary.in_waiting_list)
|
2148 |
1947 |
if len(event_ids) > 1:
|
2149 |
|
response = {
|
2150 |
|
'err': 4,
|
2151 |
|
'err_class': 'can not resize multi event booking',
|
2152 |
|
'err_desc': _('can not resize multi event booking'),
|
2153 |
|
}
|
2154 |
|
return Response(response)
|
|
1948 |
raise APIError(N_('can not resize multi event booking'), err=4)
|
2155 |
1949 |
if len(in_waiting_list) > 1:
|
2156 |
|
response = {
|
2157 |
|
'err': 5,
|
2158 |
|
'err_class': 'can not resize booking: waiting list inconsistency',
|
2159 |
|
'err_desc': _('can not resize booking: waiting list inconsistency'),
|
2160 |
|
}
|
2161 |
|
return Response(response)
|
|
1950 |
raise APIError(N_('can not resize booking: waiting list inconsistency'), err=5)
|
2162 |
1951 |
|
2163 |
1952 |
# total places for the event (in waiting or main list, depending on the primary booking location)
|
2164 |
1953 |
places = event.waiting_list_places if booking.in_waiting_list else event.places
|
... | ... | |
2182 |
1971 |
# oversized request
|
2183 |
1972 |
if booking.in_waiting_list:
|
2184 |
1973 |
# booking in waiting list: can not be overbooked
|
2185 |
|
response = {
|
2186 |
|
'err': 3,
|
2187 |
|
'err_class': 'sold out',
|
2188 |
|
'err_desc': _('sold out'),
|
2189 |
|
}
|
2190 |
|
return Response(response)
|
|
1974 |
raise APIError(N_('sold out'), err=3)
|
2191 |
1975 |
if event.booked_places <= event.places:
|
2192 |
1976 |
# in main list and no overbooking for the moment: can not be overbooked
|
2193 |
|
response = {
|
2194 |
|
'err': 3,
|
2195 |
|
'err_class': 'sold out',
|
2196 |
|
'err_desc': _('sold out'),
|
2197 |
|
}
|
2198 |
|
return Response(response)
|
|
1977 |
raise APIError(N_('sold out'), err=3)
|
2199 |
1978 |
return self.increase(booking, secondary_bookings, primary_booked_places, primary_wanted_places)
|
2200 |
1979 |
|
2201 |
1980 |
def increase(self, booking, secondary_bookings, primary_booked_places, primary_wanted_places):
|
... | ... | |
2245 |
2024 |
|
2246 |
2025 |
serializer = self.serializer_class(data=request.data)
|
2247 |
2026 |
if not serializer.is_valid():
|
2248 |
|
raise APIError(
|
2249 |
|
_('invalid payload'),
|
2250 |
|
err_class='invalid payload',
|
2251 |
|
errors=serializer.errors,
|
2252 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
2253 |
|
)
|
|
2027 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
2254 |
2028 |
payload = serializer.validated_data
|
2255 |
2029 |
event = Event.objects.create(agenda=agenda, **payload)
|
2256 |
2030 |
if event.recurrence_days and event.recurrence_end_date:
|
... | ... | |
2261 |
2035 |
event = self.get_object(agenda_identifier, event_identifier)
|
2262 |
2036 |
serializer = self.serializer_class(event, data=request.data, partial=True)
|
2263 |
2037 |
if not serializer.is_valid():
|
2264 |
|
raise APIError(
|
2265 |
|
_('invalid payload'),
|
2266 |
|
err_class='invalid payload',
|
2267 |
|
errors=serializer.errors,
|
2268 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
2269 |
|
)
|
|
2038 |
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
|
2270 |
2039 |
|
2271 |
2040 |
payload = serializer.validated_data
|
2272 |
2041 |
changed_data = []
|
... | ... | |
2282 |
2051 |
'recurrence_days',
|
2283 |
2052 |
'recurrence_week_interval',
|
2284 |
2053 |
):
|
2285 |
|
raise APIError(
|
2286 |
|
_('%s cannot be modified on an event recurrence') % field,
|
2287 |
|
err_class='%s cannot be modified on an event recurrence' % field,
|
2288 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
2289 |
|
)
|
|
2054 |
raise APIErrorBadRequest(N_('%s cannot be modified on an event recurrence') % field)
|
2290 |
2055 |
|
2291 |
2056 |
protected_fields = ['start_datetime', 'recurrence_days', 'recurrence_week_interval']
|
2292 |
2057 |
if event.recurrence_days and event.has_recurrences_booked():
|
2293 |
2058 |
for field in changed_data:
|
2294 |
2059 |
if field in protected_fields:
|
2295 |
|
raise APIError(
|
2296 |
|
_('%s cannot be modified because some recurrences have bookings attached to them.')
|
2297 |
|
% field,
|
2298 |
|
err_class='%s cannot be modified because some recurrences have bookings attached to them.'
|
2299 |
|
% field,
|
2300 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
|
2060 |
raise APIErrorBadRequest(
|
|
2061 |
N_('%s cannot be modified because some recurrences have bookings attached to them.')
|
|
2062 |
% field
|
2301 |
2063 |
)
|
2302 |
2064 |
if 'recurrence_end_date' in changed_data and event.has_recurrences_booked(
|
2303 |
2065 |
after=payload['recurrence_end_date']
|
2304 |
2066 |
):
|
2305 |
|
raise APIError(
|
2306 |
|
_('recurrence_end_date cannot be modified because bookings exist after this date.'),
|
2307 |
|
err_class='recurrence_end_date cannot be modified because bookings exist after this date.',
|
2308 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
|
2067 |
raise APIErrorBadRequest(
|
|
2068 |
N_('recurrence_end_date cannot be modified because bookings exist after this date.')
|
2309 |
2069 |
)
|
2310 |
2070 |
|
2311 |
2071 |
with event.update_recurrences(
|
... | ... | |
2392 |
2152 |
|
2393 |
2153 |
def get(self, request, agenda_identifier=None, event_identifier=None, format=None):
|
2394 |
2154 |
if not request.GET.get('user_external_id'):
|
2395 |
|
response = {
|
2396 |
|
'err': 1,
|
2397 |
|
'err_class': 'missing param user_external_id',
|
2398 |
|
'err_desc': _('missing param user_external_id'),
|
2399 |
|
}
|
2400 |
|
return Response(response)
|
|
2155 |
raise APIError(N_('missing param user_external_id'))
|
2401 |
2156 |
event = self.get_object(agenda_identifier, event_identifier)
|
2402 |
2157 |
booking_queryset = event.booking_set.filter(
|
2403 |
2158 |
user_external_id=request.GET['user_external_id'],
|
... | ... | |
2497 |
2252 |
def get(self, request, *args, **kwargs):
|
2498 |
2253 |
serializer = self.serializer_class(data=request.query_params)
|
2499 |
2254 |
if not serializer.is_valid():
|
2500 |
|
raise APIError(
|
2501 |
|
_('invalid statistics filters'),
|
2502 |
|
err_class='invalid statistics filters',
|
2503 |
|
errors=serializer.errors,
|
2504 |
|
http_status=status.HTTP_400_BAD_REQUEST,
|
2505 |
|
)
|
|
2255 |
raise APIErrorBadRequest(N_('invalid statistics filters'), errors=serializer.errors)
|
2506 |
2256 |
data = serializer.validated_data
|
2507 |
2257 |
|
2508 |
2258 |
bookings = Booking.objects
|