manager: filter timesheet by booking status (#84260)
gitea/chrono/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2023-12-05 12:22:22 +01:00 committed by Lauréline Guérin
parent d02210ab66
commit 698bbfc7a4
2 changed files with 245 additions and 31 deletions

View File

@ -772,17 +772,29 @@ class EventsTimesheetForm(forms.Form):
],
initial='portrait',
)
booking_filter = forms.ChoiceField(
label=_('Filter by status'),
choices=[
('all', _('All')),
('with_booking', _('With booking')),
('without_booking', _('Without booking')),
],
initial='all',
)
def __init__(self, *args, **kwargs):
self.agenda = kwargs.pop('agenda')
self.event = kwargs.pop('event', None)
super().__init__(*args, **kwargs)
self.with_subscriptions = self.agenda.subscriptions.exists()
if self.event is not None:
del self.fields['date_start']
del self.fields['date_end']
del self.fields['date_display']
del self.fields['custom_nb_dates_per_page']
del self.fields['activity_display']
if not self.with_subscriptions:
del self.fields['booking_filter']
def get_slots(self):
extra_data = self.cleaned_data['extra_data'].split(',')
@ -850,20 +862,21 @@ class EventsTimesheetForm(forms.Form):
)
users = {}
subscriptions = self.agenda.subscriptions.filter(date_start__lt=max_start, date_end__gt=min_start)
for subscription in subscriptions:
if subscription.user_external_id in users:
continue
users[subscription.user_external_id] = {
'user_id': subscription.user_external_id,
'user_first_name': subscription.user_first_name,
'user_last_name': subscription.user_last_name,
'extra_data': {k: (subscription.extra_data or {}).get(k) or '' for k in all_extra_data},
'events': copy.deepcopy(event_slots),
}
if self.with_subscriptions:
subscriptions = self.agenda.subscriptions.filter(date_start__lt=max_start, date_end__gt=min_start)
for subscription in subscriptions:
if subscription.user_external_id in users:
continue
users[subscription.user_external_id] = {
'user_id': subscription.user_external_id,
'user_first_name': subscription.user_first_name,
'user_last_name': subscription.user_last_name,
'extra_data': {k: (subscription.extra_data or {}).get(k) or '' for k in all_extra_data},
'events': copy.deepcopy(event_slots),
}
booking_qs_kwargs = {}
if not self.agenda.subscriptions.exists():
if not self.with_subscriptions:
booking_qs_kwargs = {'cancellation_datetime__isnull': True}
booked_qs = (
Booking.objects.filter(
@ -899,6 +912,19 @@ class EventsTimesheetForm(forms.Form):
participants += 1
break
if self.cleaned_data.get('booking_filter') == 'with_booking':
# remove subscribed users without booking
users = {
k: user for k, user in users.items() if any(any(e['dates'].values()) for e in user['events'])
}
elif self.cleaned_data.get('booking_filter') == 'without_booking':
# remove subscribed users with booking
users = {
k: user
for k, user in users.items()
if not any(any(e['dates'].values()) for e in user['events'])
}
if self.cleaned_data['sort'] == 'lastname,firstname':
sort_fields = ['user_last_name', 'user_first_name']
else:

View File

@ -266,6 +266,113 @@ def test_events_timesheet_subscription_limits(app, admin_user):
assert users[7]['user_id'] == 'user:2022-02-28-2022-03-01'
@pytest.mark.freeze_time('2022-02-15')
def test_events_timesheet_filter_with_booking(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event1 = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2022, 2, 1, 17, 0)), places=10, agenda=agenda
)
event2 = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda
)
event3 = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2022, 2, 28, 17, 0)), places=10, agenda=agenda
)
Booking.objects.create(
event=event1, user_external_id='user:1', user_first_name='User', user_last_name='1'
)
Booking.objects.create(
event=event1, user_external_id='user:2', user_first_name='User', user_last_name='2'
)
Booking.objects.create(
event=event2, user_external_id='user:2', user_first_name='User', user_last_name='2'
)
login(app)
resp = app.get('/manage/agendas/%s/events/timesheet' % agenda.pk)
assert 'booking_filter' not in resp.context['form'].fields
start = datetime.date(2022, 2, 1)
end = datetime.date(2022, 3, 1)
for user_id in [1, 2, 3, 4, 5, 6]:
Subscription.objects.create(
agenda=agenda,
user_external_id='user:%s' % user_id,
user_first_name='Subscription',
user_last_name=str(user_id),
date_start=start,
date_end=end,
)
resp = app.get('/manage/agendas/%s/events/timesheet' % agenda.pk)
resp.form['date_start'] = '2022-02-01'
resp.form['date_end'] = '2022-02-28'
assert resp.form['booking_filter'].value == 'all'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 28),
]
assert slots['events'] == [
event1,
event2,
event3,
]
users = slots['users'][0]['users']
assert len(users) == 6
assert users[0]['user_id'] == 'user:1'
assert users[1]['user_id'] == 'user:2'
assert users[2]['user_id'] == 'user:3'
assert users[3]['user_id'] == 'user:4'
assert users[4]['user_id'] == 'user:5'
assert users[5]['user_id'] == 'user:6'
resp.form['booking_filter'] = 'with_booking'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 28),
]
assert slots['events'] == [
event1,
event2,
event3,
]
users = slots['users'][0]['users']
assert len(users) == 2
assert users[0]['user_id'] == 'user:1'
assert users[1]['user_id'] == 'user:2'
resp.form['booking_filter'] = 'without_booking'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 28),
]
assert slots['events'] == [
event1,
event2,
event3,
]
users = slots['users'][0]['users']
assert len(users) == 4
assert users[0]['user_id'] == 'user:3'
assert users[1]['user_id'] == 'user:4'
assert users[2]['user_id'] == 'user:5'
assert users[3]['user_id'] == 'user:6'
def test_events_timesheet_users(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
@ -959,7 +1066,7 @@ def test_events_timesheet_pdf(app, admin_user):
login(app)
resp = app.get(
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=row&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=row&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.headers['Content-Type'] == 'application/pdf'
assert (
@ -969,8 +1076,8 @@ def test_events_timesheet_pdf(app, admin_user):
# form invalid
resp = app.get(
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname&date_display=all&activity_display=row'
% agenda.pk
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname&date_display=all'
'&activity_display=row&booking_filter=all' % agenda.pk
)
assert resp.context['form'].errors['orientation'] == ['This field is required.']
@ -981,7 +1088,7 @@ def test_events_timesheet_csv(app, admin_user):
login(app)
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=row&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=row&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.headers['Content-Type'] == 'text/csv'
assert (
@ -991,8 +1098,8 @@ def test_events_timesheet_csv(app, admin_user):
# form invalid
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname&date_display=all&activity_display=row'
% agenda.pk
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname&date_display=all'
'&activity_display=row&booking_filter=all' % agenda.pk
)
assert resp.context['form'].errors['orientation'] == ['This field is required.']
@ -1034,7 +1141,7 @@ def test_events_timesheet_csv(app, admin_user):
# display row
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=row&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=row&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.text == (
'First name,Last name,Mon 07/02,Mon 14/02,Mon 21/02,Mon 28/02\r\n'
@ -1044,7 +1151,7 @@ def test_events_timesheet_csv(app, admin_user):
# display col
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=col&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=col&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.text == (
'First name,Last name,recurring 1 of 07-02,recurring 1 of 14-02,recurring 1 of 21-02,recurring 1 of 28-02\r\n'
@ -1067,7 +1174,7 @@ def test_events_timesheet_csv(app, admin_user):
# display row
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=row&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=row&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.text == (
'First name,Last name,Activity,Mon 07/02,Mon 14/02,Tue 15/02,Mon 21/02,Mon 28/02\r\n'
@ -1079,7 +1186,7 @@ def test_events_timesheet_csv(app, admin_user):
# display col
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=col&orientation=portrait' % agenda.pk
'&date_display=all&activity_display=col&orientation=portrait&booking_filter=all' % agenda.pk
)
assert resp.text == (
'First name,Last name,recurring 1 of 07-02,recurring 1 of 14-02,"Feb. 15, 2022, 5 p.m. of 15-02",recurring 1 of 21-02,recurring 1 of 28-02\r\n'
@ -1091,7 +1198,8 @@ def test_events_timesheet_csv(app, admin_user):
# display row
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=row&orientation=portrait&extra_data=foo&group_by=foo' % agenda.pk
'&date_display=all&activity_display=row&orientation=portrait&booking_filter=all&extra_data=foo&group_by=foo'
% agenda.pk
)
assert resp.text == (
'foo: bar\r\n'
@ -1104,7 +1212,8 @@ def test_events_timesheet_csv(app, admin_user):
# display col
resp = app.get(
'/manage/agendas/%s/events/timesheet?csv=&date_start=2022-02-01&date_end=2022-02-28&sort=lastname,firstname'
'&date_display=all&activity_display=col&orientation=portrait&extra_data=foo&group_by=foo' % agenda.pk
'&date_display=all&activity_display=col&orientation=portrait&booking_filter=all&extra_data=foo&group_by=foo'
% agenda.pk
)
assert resp.text == (
'foo: bar\r\n'
@ -1277,6 +1386,85 @@ def test_event_timesheet_subscription_limits(app, admin_user):
assert users[3]['user_id'] == 'user:2022-02-15-2022-03-01'
@pytest.mark.freeze_time('2022-02-15')
def test_event_timesheet_filter_with_booking(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda
)
Booking.objects.create(event=event, user_external_id='user:1', user_first_name='User', user_last_name='1')
Booking.objects.create(event=event, user_external_id='user:2', user_first_name='User', user_last_name='2')
login(app)
resp = app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk))
assert 'booking_filter' not in resp.context['form'].fields
start = datetime.date(2022, 2, 1)
end = datetime.date(2022, 3, 1)
for user_id in [1, 2, 3, 4, 5, 6]:
Subscription.objects.create(
agenda=agenda,
user_external_id='user:%s' % user_id,
user_first_name='Subscription',
user_last_name=str(user_id),
date_start=start,
date_end=end,
)
resp = app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk))
assert resp.form['booking_filter'].value == 'all'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 15),
]
assert slots['events'] == [
event,
]
users = slots['users'][0]['users']
assert len(users) == 6
assert users[0]['user_id'] == 'user:1'
assert users[1]['user_id'] == 'user:2'
assert users[2]['user_id'] == 'user:3'
assert users[3]['user_id'] == 'user:4'
assert users[4]['user_id'] == 'user:5'
assert users[5]['user_id'] == 'user:6'
resp.form['booking_filter'] = 'with_booking'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 15),
]
assert slots['events'] == [
event,
]
users = slots['users'][0]['users']
assert len(users) == 2
assert users[0]['user_id'] == 'user:1'
assert users[1]['user_id'] == 'user:2'
resp.form['booking_filter'] = 'without_booking'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['dates']) == 1
assert [d[0] for d in slots['dates'][0]] == [
datetime.date(2022, 2, 15),
]
assert slots['events'] == [
event,
]
users = slots['users'][0]['users']
assert len(users) == 4
assert users[0]['user_id'] == 'user:3'
assert users[1]['user_id'] == 'user:4'
assert users[2]['user_id'] == 'user:5'
assert users[3]['user_id'] == 'user:6'
def test_event_timesheet_pdf(app, admin_user):
agenda = Agenda.objects.create(label='Foo', kind='events')
event = Event.objects.create(
@ -1288,7 +1476,7 @@ def test_event_timesheet_pdf(app, admin_user):
login(app)
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all&orientation=portrait'
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all&orientation=portrait&booking_filter=all'
% (agenda.pk, event.pk)
)
assert resp.headers['Content-Type'] == 'application/pdf'
@ -1296,7 +1484,7 @@ def test_event_timesheet_pdf(app, admin_user):
# form invalid
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all'
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all&booking_filter=all'
% (agenda.pk, event.pk)
)
assert resp.context['form'].errors['orientation'] == ['This field is required.']
@ -1313,7 +1501,7 @@ def test_event_timesheet_csv(app, admin_user):
login(app)
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait'
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait&booking_filter=all'
% (agenda.pk, event.pk)
)
assert resp.headers['Content-Type'] == 'text/csv'
@ -1321,7 +1509,7 @@ def test_event_timesheet_csv(app, admin_user):
# form invalid
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all'
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&booking_filter=all'
% (agenda.pk, event.pk)
)
assert resp.context['form'].errors['orientation'] == ['This field is required.']
@ -1351,14 +1539,14 @@ def test_event_timesheet_csv(app, admin_user):
user_last_name='02',
)
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait'
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait&booking_filter=all'
% (agenda.pk, event.pk)
)
assert resp.text == ('First name,Last name,Tue 15/02\r\n' 'Subscription,01,\r\n' 'Subscription,02,☐\r\n')
resp = app.get(
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait&extra_data=foo&group_by=foo'
% (agenda.pk, event.pk)
'/manage/agendas/%s/events/%s/timesheet?csv=&sort=lastname,firstname&date_display=all&orientation=portrait'
'&booking_filter=all&extra_data=foo&group_by=foo' % (agenda.pk, event.pk)
)
assert resp.text == (
'foo: bar\r\n'