0001-api-mark-user-as-present-or-not-38678.patch
chrono/agendas/migrations/0071_booking_attendance.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('agendas', '0070_auto_20201202_1834'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='booking', |
|
16 |
name='user_was_present', |
|
17 |
field=models.NullBooleanField(), |
|
18 |
), |
|
19 |
] |
chrono/agendas/models.py | ||
---|---|---|
1098 | 1098 |
user_name = models.CharField(max_length=250, blank=True) |
1099 | 1099 |
user_email = models.EmailField(blank=True) |
1100 | 1100 |
user_phone_number = models.CharField(max_length=16, blank=True) |
1101 |
user_was_present = models.NullBooleanField() |
|
1102 | ||
1101 | 1103 |
form_url = models.URLField(blank=True) |
1102 | 1104 |
backoffice_url = models.URLField(blank=True) |
1103 | 1105 |
cancel_callback_url = models.URLField(blank=True) |
chrono/api/urls.py | ||
---|---|---|
55 | 55 |
views.meeting_datetimes, |
56 | 56 |
name='api-agenda-meeting-datetimes', |
57 | 57 |
), |
58 |
url(r'^booking/(?P<booking_pk>\w+)/$', views.booking), |
|
58 |
url(r'^booking/(?P<booking_pk>\w+)/$', views.booking, name='api-booking'),
|
|
59 | 59 |
url(r'^booking/(?P<booking_pk>\w+)/cancel/$', views.cancel_booking, name='api-cancel-booking'), |
60 | 60 |
url(r'^booking/(?P<booking_pk>\w+)/accept/$', views.accept_booking, name='api-accept-booking'), |
61 | 61 |
url(r'^booking/(?P<booking_pk>\w+)/suspend/$', views.suspend_booking, name='api-suspend-booking'), |
chrono/api/views.py | ||
---|---|---|
1093 | 1093 |
'slug': primary_booking.event.agenda.slug, |
1094 | 1094 |
}, |
1095 | 1095 |
'api': { |
1096 |
'booking_url': request.build_absolute_uri( |
|
1097 |
reverse('api-booking', kwargs={'booking_pk': primary_booking.id}) |
|
1098 |
), |
|
1096 | 1099 |
'cancel_url': request.build_absolute_uri( |
1097 | 1100 |
reverse('api-cancel-booking', kwargs={'booking_pk': primary_booking.id}) |
1098 | 1101 |
), |
... | ... | |
1162 | 1165 |
fillslot = Fillslot.as_view() |
1163 | 1166 | |
1164 | 1167 | |
1168 |
class BookingSerializer(serializers.ModelSerializer): |
|
1169 |
class Meta: |
|
1170 |
model = Booking |
|
1171 |
fields = ['user_was_present'] |
|
1172 | ||
1173 | ||
1165 | 1174 |
class BookingAPI(APIView): |
1166 | 1175 |
permission_classes = (permissions.IsAuthenticated,) |
1176 |
serializer_class = BookingSerializer |
|
1167 | 1177 | |
1168 | 1178 |
def initial(self, request, *args, **kwargs): |
1169 |
super(BookingAPI, self).initial(request, *args, **kwargs) |
|
1170 |
self.booking = Booking.objects.get(id=kwargs.get('booking_pk'), cancellation_datetime__isnull=True) |
|
1179 |
super().initial(request, *args, **kwargs) |
|
1180 |
self.booking = get_object_or_404(Booking, pk=kwargs.get('booking_pk')) |
|
1181 | ||
1182 |
def check_booking(self, check_waiting_list=False): |
|
1183 |
if self.booking.cancellation_datetime: |
|
1184 |
return Response( |
|
1185 |
{'err': 1, 'err_class': 'booking is cancelled', 'err_desc': _('booking is cancelled')} |
|
1186 |
) |
|
1187 | ||
1188 |
if self.booking.primary_booking is not None: |
|
1189 |
return Response({'err': 2, 'err_class': 'secondary booking', 'err_desc': _('secondary booking')}) |
|
1190 | ||
1191 |
if check_waiting_list and self.booking.in_waiting_list: |
|
1192 |
response = { |
|
1193 |
'err': 3, |
|
1194 |
'err_class': 'booking is in waiting list', |
|
1195 |
'err_desc': _('booking is in waiting list'), |
|
1196 |
} |
|
1197 |
return Response(response) |
|
1198 | ||
1199 |
def get(self, request, *args, **kwargs): |
|
1200 |
response = self.check_booking() |
|
1201 |
if response: |
|
1202 |
return response |
|
1203 | ||
1204 |
response = { |
|
1205 |
'err': 0, |
|
1206 |
'booking_id': self.booking.pk, |
|
1207 |
'in_waiting_list': self.booking.in_waiting_list, |
|
1208 |
'user_was_present': self.booking.user_was_present, |
|
1209 |
} |
|
1210 |
return Response(response) |
|
1211 | ||
1212 |
def patch(self, request, *args, **kwargs): |
|
1213 |
response = self.check_booking(check_waiting_list=True) |
|
1214 |
if response: |
|
1215 |
return response |
|
1216 | ||
1217 |
serializer = self.serializer_class(self.booking, data=request.data, partial=True) |
|
1218 | ||
1219 |
if not serializer.is_valid(): |
|
1220 |
return Response( |
|
1221 |
{ |
|
1222 |
'err': 4, |
|
1223 |
'err_class': 'invalid payload', |
|
1224 |
'err_desc': _('invalid payload'), |
|
1225 |
'errors': serializer.errors, |
|
1226 |
}, |
|
1227 |
status=status.HTTP_400_BAD_REQUEST, |
|
1228 |
) |
|
1229 | ||
1230 |
serializer.save() |
|
1231 |
self.booking.secondary_booking_set.update(user_was_present=self.booking.user_was_present) |
|
1232 | ||
1233 |
response = {'err': 0, 'booking_id': self.booking.pk} |
|
1234 |
return Response(response) |
|
1171 | 1235 | |
1172 | 1236 |
def delete(self, request, *args, **kwargs): |
1237 |
response = self.check_booking() |
|
1238 |
if response: |
|
1239 |
return response |
|
1240 | ||
1173 | 1241 |
self.booking.cancel() |
1174 |
response = {'err': 0, 'booking_id': self.booking.id}
|
|
1242 |
response = {'err': 0, 'booking_id': self.booking.pk}
|
|
1175 | 1243 |
return Response(response) |
1176 | 1244 | |
1177 | 1245 |
tests/test_api.py | ||
---|---|---|
909 | 909 |
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id)) |
910 | 910 |
Booking.objects.get(id=resp.json['booking_id']) |
911 | 911 |
assert resp.json['datetime'] == localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S') |
912 |
assert 'booking_url' in resp.json['api'] |
|
912 | 913 |
assert 'accept_url' in resp.json['api'] |
913 | 914 |
assert 'suspend_url' in resp.json['api'] |
914 | 915 |
assert 'cancel_url' in resp.json['api'] |
915 | 916 |
assert 'ics_url' in resp.json['api'] |
917 |
assert urlparse.urlparse(resp.json['api']['booking_url']).netloc |
|
918 |
assert urlparse.urlparse(resp.json['api']['accept_url']).netloc |
|
916 | 919 |
assert urlparse.urlparse(resp.json['api']['suspend_url']).netloc |
917 | 920 |
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc |
918 | 921 |
assert urlparse.urlparse(resp.json['api']['ics_url']).netloc |
... | ... | |
1117 | 1120 |
primary_booking_id = resp.json['booking_id'] |
1118 | 1121 |
Booking.objects.get(id=primary_booking_id) |
1119 | 1122 |
assert resp.json['datetime'] == localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S') |
1123 |
assert 'booking_url' in resp.json['api'] |
|
1120 | 1124 |
assert 'accept_url' in resp.json['api'] |
1121 | 1125 |
assert 'suspend_url' in resp.json['api'] |
1122 | 1126 |
assert 'cancel_url' in resp.json['api'] |
1127 |
assert urlparse.urlparse(resp.json['api']['booking_url']).netloc |
|
1128 |
assert urlparse.urlparse(resp.json['api']['accept_url']).netloc |
|
1123 | 1129 |
assert urlparse.urlparse(resp.json['api']['suspend_url']).netloc |
1124 | 1130 |
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc |
1125 | 1131 |
assert Booking.objects.count() == 3 |
... | ... | |
1904 | 1910 |
assert Booking.objects.count() == 6 |
1905 | 1911 | |
1906 | 1912 | |
1913 |
@pytest.mark.parametrize('flag', [True, False, None]) |
|
1914 |
def test_booking_api_present(app, user, flag): |
|
1915 |
agenda = Agenda.objects.create(kind='events') |
|
1916 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10) |
|
1917 |
booking = Booking.objects.create(event=event, user_was_present=flag) |
|
1918 | ||
1919 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
1920 |
resp = app.get('/api/booking/%s/' % booking.pk) |
|
1921 |
assert resp.json['booking_id'] == booking.pk |
|
1922 |
assert resp.json['user_was_present'] == flag |
|
1923 | ||
1924 | ||
1925 |
@pytest.mark.parametrize('flag', [True, False]) |
|
1926 |
def test_booking_api_waiting_list(app, user, flag): |
|
1927 |
agenda = Agenda.objects.create(kind='events') |
|
1928 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10) |
|
1929 |
booking = Booking.objects.create(event=event, in_waiting_list=flag) |
|
1930 | ||
1931 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
1932 | ||
1933 |
resp = app.get('/api/booking/%s/' % booking.pk) |
|
1934 |
assert resp.json['booking_id'] == booking.pk |
|
1935 |
assert resp.json['in_waiting_list'] == flag |
|
1936 | ||
1937 | ||
1938 |
def test_booking_api_error(app, user): |
|
1939 |
agenda = Agenda.objects.create(kind='events') |
|
1940 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10) |
|
1941 |
booking = Booking.objects.create(event=event) |
|
1942 | ||
1943 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
1944 | ||
1945 |
# unknown |
|
1946 |
booking.cancel() |
|
1947 |
for method in ['get', 'delete', 'patch']: |
|
1948 |
getattr(app, method)('/api/booking/0/', status=404) |
|
1949 | ||
1950 |
# cancelled |
|
1951 |
booking.cancel() |
|
1952 |
for method in ['get', 'delete', 'patch']: |
|
1953 |
resp = getattr(app, method)('/api/booking/%s/' % booking.pk) |
|
1954 |
assert resp.json['err'] == 1 |
|
1955 |
assert resp.json['err_desc'] == 'booking is cancelled' |
|
1956 | ||
1957 |
# not a primary booking |
|
1958 |
secondary = Booking.objects.create(event=event, primary_booking=booking) |
|
1959 |
for method in ['get', 'delete', 'patch']: |
|
1960 |
resp = getattr(app, method)('/api/booking/%s/' % secondary.pk) |
|
1961 |
assert resp.json['err'] == 2 |
|
1962 |
assert resp.json['err_desc'] == 'secondary booking' |
|
1963 | ||
1964 |
# in waiting list |
|
1965 |
booking.cancellation_datetime = None |
|
1966 |
booking.in_waiting_list = True |
|
1967 |
booking.save() |
|
1968 |
resp = app.get('/api/booking/%s/' % booking.pk) |
|
1969 |
assert resp.json['err'] == 0 |
|
1970 |
resp = app.patch('/api/booking/%s/' % booking.pk) |
|
1971 |
assert resp.json['err'] == 3 |
|
1972 |
assert resp.json['err_desc'] == 'booking is in waiting list' |
|
1973 |
resp = app.delete('/api/booking/%s/' % booking.pk) |
|
1974 |
assert resp.json['err'] == 0 |
|
1975 | ||
1976 | ||
1977 |
def test_booking_patch_api(app, user): |
|
1978 |
agenda = Agenda.objects.create(kind='events') |
|
1979 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10) |
|
1980 |
booking = Booking.objects.create(event=event) |
|
1981 | ||
1982 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
1983 | ||
1984 |
resp = app.patch('/api/booking/%s/' % booking.pk) |
|
1985 |
assert resp.json['err'] == 0 |
|
1986 |
resp = app.patch('/api/booking/%s/' % booking.pk, params={'user_was_present': 'foobar'}, status=400) |
|
1987 |
assert resp.json['err'] == 4 |
|
1988 |
assert resp.json['err_desc'] == 'invalid payload' |
|
1989 | ||
1990 | ||
1991 |
@pytest.mark.parametrize('flag', [True, False, None]) |
|
1992 |
def test_booking_patch_api_present(app, user, flag): |
|
1993 |
agenda = Agenda.objects.create(kind='events') |
|
1994 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10) |
|
1995 |
booking = Booking.objects.create(event=event) |
|
1996 | ||
1997 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
1998 | ||
1999 |
# set flag |
|
2000 |
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': flag}) |
|
2001 |
booking.refresh_from_db() |
|
2002 |
assert booking.user_was_present == flag |
|
2003 | ||
2004 |
# reset |
|
2005 |
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': None}) |
|
2006 |
booking.refresh_from_db() |
|
2007 |
assert booking.user_was_present is None |
|
2008 | ||
2009 |
# make secondary bookings |
|
2010 |
Booking.objects.create(event=event, primary_booking=booking) |
|
2011 |
Booking.objects.create(event=event, primary_booking=booking) |
|
2012 |
# and other booking |
|
2013 |
other_booking = Booking.objects.create(event=event) |
|
2014 | ||
2015 |
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': flag}) |
|
2016 |
booking.refresh_from_db() |
|
2017 |
assert booking.user_was_present == flag |
|
2018 |
# all secondary bookings are upadted |
|
2019 |
assert list(booking.secondary_booking_set.values_list('user_was_present', flat=True)) == [flag, flag] |
|
2020 |
other_booking.refresh_from_db() |
|
2021 |
assert other_booking.user_was_present is None # not changed |
|
2022 | ||
2023 | ||
1907 | 2024 |
def test_booking_cancellation_api(app, some_data, user): |
1908 | 2025 |
agenda = Agenda.objects.filter(label=u'Foo bar')[0] |
1909 | 2026 |
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0] |
1910 |
- |