manager: timesheet grouper (#61920)

This commit is contained in:
Lauréline Guérin 2022-02-18 16:54:41 +01:00
parent da928de65a
commit 0625b88536
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
4 changed files with 155 additions and 39 deletions

View File

@ -376,6 +376,16 @@ class EventsTimesheetForm(forms.Form):
required=False,
help_text=_('Comma separated list of keys defined in extra_data.'),
)
group_by = forms.CharField(
label=_('Group by'),
max_length=50,
required=False,
help_text=_('Key defined in extra_data.'),
)
with_page_break = forms.BooleanField(
label=_('Add a page break before earh grouper'),
required=False,
)
date_display = forms.ChoiceField(
label=_('Display'),
choices=[
@ -406,6 +416,10 @@ class EventsTimesheetForm(forms.Form):
def get_slots(self):
extra_data = self.cleaned_data['extra_data'].split(',')
extra_data = [d.strip() for d in extra_data if d.strip()]
group_by = self.cleaned_data['group_by'].strip()
all_extra_data = extra_data[:]
if group_by:
all_extra_data += [group_by]
min_start = make_aware(
datetime.datetime.combine(self.cleaned_data['date_start'], datetime.time(0, 0))
)
@ -466,7 +480,7 @@ class EventsTimesheetForm(forms.Form):
'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 extra_data},
'extra_data': {k: (subscription.extra_data or {}).get(k) or '' for k in all_extra_data},
'events': copy.deepcopy(event_slots),
}
@ -491,7 +505,7 @@ class EventsTimesheetForm(forms.Form):
'user_id': user_id,
'user_first_name': booking.user_first_name,
'user_last_name': booking.user_last_name,
'extra_data': {k: (booking.extra_data or {}).get(k) or '' for k in extra_data},
'extra_data': {k: (booking.extra_data or {}).get(k) or '' for k in all_extra_data},
'events': copy.deepcopy(event_slots),
}
if booking.cancellation_datetime is not None:
@ -505,7 +519,29 @@ class EventsTimesheetForm(forms.Form):
event['dates'][date] = True
break
users = sorted(users.values(), key=itemgetter('user_last_name', 'user_first_name', 'user_id'))
if group_by:
groupers = defaultdict(list)
for user in users.values():
groupers[user['extra_data'].get(group_by) or ''].append(user)
users = [
{
'grouper': g,
'users': sorted(u, key=itemgetter('user_last_name', 'user_first_name', 'user_id')),
}
for g, u in groupers.items()
]
users = sorted(users, key=itemgetter('grouper'))
if users and users[0]['grouper'] == '':
users = users[1:] + users[:1]
else:
users = [
{
'grouper': '',
'users': sorted(
users.values(), key=itemgetter('user_last_name', 'user_first_name', 'user_id')
),
}
]
return {
'dates': dates,

View File

@ -24,6 +24,14 @@
}
});
$('#id_date_display').trigger('change');
$('#id_group_by').on('change', function() {
if ($(this).val()) {
$('#id_with_page_break').parent().show();
} else {
$('#id_with_page_break').parent().hide();
}
});
$('#id_group_by').trigger('change');
});
</script>
<button class="submit-button">{% trans "See timesheet" %}</button>

View File

@ -3,6 +3,8 @@
{% with slots=form.get_slots %}
{% with events_num=slots.events|length %}
{% for dates in slots.dates %}
{% for grouper in slots.users %}
{% if form.cleaned_data.group_by %}<h5>{{ form.cleaned_data.group_by }}: {{ grouper.grouper }}</h5>{% endif %}
<table class="main timesheet">
<thead>
<tr>
@ -14,7 +16,7 @@
</tr>
</thead>
<tbody>
{% for user in slots.users %}{% for event in user.events %}
{% for user in grouper.users %}{% for event in user.events %}
<tr>
{% if forloop.first %}
<td {% if events_num > 1 %}rowspan="{{ events_num }}"{% endif %}>{{ user.user_first_name }}</td>
@ -29,7 +31,9 @@
{% endfor %}{% endfor %}
</tbody>
</table>
{% if not forloop.last %}<div class="page_break"></div>{% endif %}
{% if form.cleaned_data.with_page_break %}<div class="page_break"></div>{% endif %}
{% endfor %}
{% if not form.cleaned_data.with_page_break %}<div class="page_break"></div>{% endif %}
{% endfor %}
{% endwith %}
{% endwith %}

View File

@ -2120,7 +2120,7 @@ def test_events_timesheet_slots(app, admin_user):
event3,
event4,
]
assert slots['users'] == [
assert slots['users'][0]['users'] == [
{
'user_id': 'user:1',
'user_first_name': 'Subscription',
@ -2213,15 +2213,16 @@ def test_events_timesheet_subscription_limits(app, admin_user):
event2,
event3,
]
assert len(slots['users']) == 8
assert slots['users'][0]['user_id'] == 'user:2022-02-01-2022-02-02'
assert slots['users'][1]['user_id'] == 'user:2022-02-01-2022-02-15'
assert slots['users'][2]['user_id'] == 'user:2022-02-01-2022-02-16'
assert slots['users'][3]['user_id'] == 'user:2022-02-01-2022-03-01'
assert slots['users'][4]['user_id'] == 'user:2022-02-15-2022-02-28'
assert slots['users'][5]['user_id'] == 'user:2022-02-15-2022-03-01'
assert slots['users'][6]['user_id'] == 'user:2022-02-16-2022-03-01'
assert slots['users'][7]['user_id'] == 'user:2022-02-28-2022-03-01'
users = slots['users'][0]['users']
assert len(users) == 8
assert users[0]['user_id'] == 'user:2022-02-01-2022-02-02'
assert users[1]['user_id'] == 'user:2022-02-01-2022-02-15'
assert users[2]['user_id'] == 'user:2022-02-01-2022-02-16'
assert users[3]['user_id'] == 'user:2022-02-01-2022-03-01'
assert users[4]['user_id'] == 'user:2022-02-15-2022-02-28'
assert users[5]['user_id'] == 'user:2022-02-15-2022-03-01'
assert users[6]['user_id'] == 'user:2022-02-16-2022-03-01'
assert users[7]['user_id'] == 'user:2022-02-28-2022-03-01'
def test_events_timesheet_users(app, admin_user):
@ -2278,7 +2279,7 @@ def test_events_timesheet_users(app, admin_user):
resp.form['date_end'] = '2022-02-28'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert [u['user_id'] for u in slots['users']] == [
assert [u['user_id'] for u in slots['users'][0]['users']] == [
'user:2',
'user:5',
'user:3',
@ -2339,7 +2340,7 @@ def test_events_timesheet_users(app, admin_user):
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert [u['user_id'] for u in slots['users']] == [
assert [u['user_id'] for u in slots['users'][0]['users']] == [
'user:2',
'user:5',
'user:6',
@ -2366,20 +2367,20 @@ def test_events_timesheet_user_ids(app, admin_user):
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
# no user_id found
assert [u['user_id'] for u in slots['users']] == []
assert [u['user_first_name'] for u in slots['users']] == []
assert [u['user_last_name'] for u in slots['users']] == []
assert [u['user_id'] for u in slots['users'][0]['users']] == []
assert [u['user_first_name'] for u in slots['users'][0]['users']] == []
assert [u['user_last_name'] for u in slots['users'][0]['users']] == []
booking.user_external_id = 'user:1'
booking.save()
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert [u['user_id'] for u in slots['users']] == [
assert [u['user_id'] for u in slots['users'][0]['users']] == [
'user:1',
]
assert [u['user_first_name'] for u in slots['users']] == ['User']
assert [u['user_last_name'] for u in slots['users']] == ['42']
assert [u['user_first_name'] for u in slots['users'][0]['users']] == ['User']
assert [u['user_last_name'] for u in slots['users'][0]['users']] == ['42']
Subscription.objects.create(
agenda=agenda,
@ -2392,13 +2393,13 @@ def test_events_timesheet_user_ids(app, admin_user):
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert [u['user_id'] for u in slots['users']] == [
assert [u['user_id'] for u in slots['users'][0]['users']] == [
'user:1',
]
assert [u['user_first_name'] for u in slots['users']] == [
assert [u['user_first_name'] for u in slots['users'][0]['users']] == [
'Subscription',
]
assert [u['user_last_name'] for u in slots['users']] == [
assert [u['user_last_name'] for u in slots['users'][0]['users']] == [
'41',
]
@ -2480,8 +2481,8 @@ def test_events_timesheet_booked(app, admin_user):
recurring_event1,
recurring_event2,
]
assert len(slots['users']) == 1
assert slots['users'][0]['events'] == [
assert len(slots['users'][0]['users']) == 1
assert slots['users'][0]['users'][0]['events'] == [
{
'event': event1,
'dates': {datetime.date(2022, 2, 15): True},
@ -2527,25 +2528,27 @@ def test_events_timesheet_extra_data(app, admin_user):
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert slots['users'][0]['grouper'] == ''
assert len(slots['users'][0]['users']) == 1
assert slots['extra_data'] == ['foo']
assert slots['users'][0]['extra_data']['foo'] == 'bar'
assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'bar'
resp.form['extra_data'] = ' foo ,baz,,'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert len(slots['users'][0]['users']) == 1
assert slots['extra_data'] == ['foo', 'baz']
assert slots['users'][0]['extra_data']['foo'] == 'bar'
assert slots['users'][0]['extra_data']['baz'] == 'blah'
assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'bar'
assert slots['users'][0]['users'][0]['extra_data']['baz'] == 'blah'
resp.form['extra_data'] = 'unknown'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert len(slots['users'][0]['users']) == 1
assert slots['extra_data'] == ['unknown']
assert slots['users'][0]['extra_data']['unknown'] == ''
assert slots['users'][0]['users'][0]['extra_data']['unknown'] == ''
Subscription.objects.create(
agenda=agenda,
@ -2561,18 +2564,83 @@ def test_events_timesheet_extra_data(app, admin_user):
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert len(slots['users'][0]['users']) == 1
assert slots['extra_data'] == ['foo']
assert slots['users'][0]['extra_data']['foo'] == 'baz'
assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'baz'
resp.form['extra_data'] = ' foo ,baz,,'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert len(slots['users'][0]['users']) == 1
assert slots['extra_data'] == ['foo', 'baz']
assert slots['users'][0]['extra_data']['foo'] == 'baz'
assert slots['users'][0]['extra_data']['baz'] == ''
assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'baz'
assert slots['users'][0]['users'][0]['extra_data']['baz'] == ''
Booking.objects.create(
event=event,
user_first_name='User',
user_last_name='43',
user_external_id='user:2',
extra_data={'foo': 'bar', 'baz': 'aa'},
)
Booking.objects.create(
event=event,
user_first_name='User',
user_last_name='44',
user_external_id='user:3',
extra_data={'foo': 'bar2', 'baz': 'aa'},
)
resp.form['extra_data'] = ''
resp.form['group_by'] = 'foo'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 3
assert slots['users'][0]['grouper'] == 'bar'
assert len(slots['users'][0]['users']) == 1
assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
assert slots['users'][1]['grouper'] == 'bar2'
assert len(slots['users'][1]['users']) == 1
assert slots['users'][1]['users'][0]['user_id'] == 'user:3'
assert slots['users'][2]['grouper'] == 'baz'
assert len(slots['users'][2]['users']) == 1
assert slots['users'][2]['users'][0]['user_id'] == 'user:1'
resp.form['group_by'] = 'baz'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 2
assert slots['users'][0]['grouper'] == 'aa'
assert len(slots['users'][0]['users']) == 2
assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
assert slots['users'][0]['users'][1]['user_id'] == 'user:3'
assert slots['users'][1]['grouper'] == ''
assert len(slots['users'][1]['users']) == 1
assert slots['users'][1]['users'][0]['user_id'] == 'user:1'
Subscription.objects.update(extra_data={'foo': 'baz', 'baz': 'blah'})
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 2
assert slots['users'][0]['grouper'] == 'aa'
assert len(slots['users'][0]['users']) == 2
assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
assert slots['users'][0]['users'][1]['user_id'] == 'user:3'
assert slots['users'][1]['grouper'] == 'blah'
assert len(slots['users'][1]['users']) == 1
assert slots['users'][1]['users'][0]['user_id'] == 'user:1'
resp.form['group_by'] = 'unknown'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert len(slots['users']) == 1
assert slots['users'][0]['grouper'] == ''
assert len(slots['users'][0]['users']) == 3
@pytest.mark.freeze_time('2022-04-01')