manager: timesheet grouper (#61920)
This commit is contained in:
parent
da928de65a
commit
0625b88536
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue