manager: timesheet and date display (#61070)

This commit is contained in:
Lauréline Guérin 2022-02-18 15:30:10 +01:00
parent 8ebbbfdfb7
commit 4b708073d4
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 241 additions and 24 deletions

View File

@ -376,6 +376,20 @@ class EventsTimesheetForm(forms.Form):
required=False,
help_text=_('Comma separated list of keys defined in extra_data.'),
)
date_display = forms.ChoiceField(
label=_('Display'),
choices=[
('all', _('All on the same page')),
('month', _('1 month per page')),
('week', _('1 week per page')),
('custom', _('Custom')),
],
initial='all',
)
custom_nb_dates_per_page = forms.IntegerField(
label=_('Number of dates per page'),
required=False,
)
orientation = forms.ChoiceField(
label=_('PDF orientation'),
choices=[
@ -421,6 +435,22 @@ class EventsTimesheetForm(forms.Form):
dates_per_event_id[real_event.pk].append(date)
dates = sorted(dates)
date_display = self.cleaned_data['date_display']
if date_display in ['month', 'week']:
grouper = defaultdict(list)
for date in dates:
if date_display == 'month':
attr = date.month
else:
attr = date.isocalendar().week
grouper[(date.year, attr)].append(date)
dates = [grouper[g] for g in sorted(grouper.keys())]
elif date_display == 'custom':
n = self.cleaned_data['custom_nb_dates_per_page']
dates = [dates[i : i + n] for i in range(0, len(dates), n)]
else:
dates = [dates]
event_slots = []
for event in events:
event_slots.append(
@ -493,6 +523,10 @@ class EventsTimesheetForm(forms.Form):
elif (cleaned_data['date_start'] + relativedelta(months=3)) < cleaned_data['date_end']:
self.add_error('date_end', _('Please select an interval of no more than 3 months.'))
if cleaned_data.get('date_display') == 'custom':
if not cleaned_data.get('custom_nb_dates_per_page'):
self.add_error('custom_nb_dates_per_page', _('This field is required.'))
return cleaned_data

View File

@ -503,3 +503,7 @@ table.timesheet {
}
}
}
.page_break {
height: 20px;
}

View File

@ -36,3 +36,8 @@ table.timesheet {
}
}
}
.page_break {
display: block;
page-break-before: always;
}

View File

@ -14,6 +14,18 @@
<div>
<form id="timesheet">
{{ form.as_p }}
<script>
$(function() {
$('#id_date_display').on('change', function() {
if ($(this).val() == 'custom') {
$('#id_custom_nb_dates_per_page').parent().show();
} else {
$('#id_custom_nb_dates_per_page').parent().hide();
}
});
$('#id_date_display').trigger('change');
});
</script>
<button class="submit-button">{% trans "See timesheet" %}</button>
{% if request.GET and form.is_valid %}
<button class="submit-button" name="pdf">{% trans "Get PDF file" %}</button>

View File

@ -2,6 +2,7 @@
{% with slots=form.get_slots %}
{% with events_num=slots.events|length %}
{% for dates in slots.dates %}
<table class="main timesheet">
<thead>
<tr>
@ -9,7 +10,7 @@
<th>{% trans "Last name" %}</th>
{% for k in slots.extra_data %}<th>{{ k }}</th>{% endfor %}
{% if events_num > 1 %}<th>{% trans "Activity" %}</th>{% endif %}
{% for date in slots.dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
{% for date in dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
</tr>
</thead>
<tbody>
@ -21,12 +22,14 @@
{% for k in slots.extra_data %}<td {% if events_num > 1 %}rowspan="{{ events_num }}"{% endif %}>{{ user.extra_data|get:k }}</td>{% endfor %}
{% endif %}
{% if events_num > 1 %}<td>{{ event.event }}</td>{% endif %}
{% for date in slots.dates %}
{% for date in dates %}
{% with booked=event.dates|get:date %}<td class="date">{% if booked is True %}☐{% elif booked is None %}-{% endif %}</td>{% endwith %}
{% endfor %}
</tr>
{% endfor %}{% endfor %}
</tbody>
</table>
{% if not forloop.last %}<div class="page_break"></div>{% endif %}
{% endfor %}
{% endwith %}
{% endwith %}

View File

@ -2098,18 +2098,20 @@ def test_events_timesheet_slots(app, admin_user):
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 2),
datetime.date(2022, 2, 7),
datetime.date(2022, 2, 8),
datetime.date(2022, 2, 9),
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 16),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 22),
datetime.date(2022, 2, 23),
datetime.date(2022, 2, 28),
[
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 2),
datetime.date(2022, 2, 7),
datetime.date(2022, 2, 8),
datetime.date(2022, 2, 9),
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 16),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 22),
datetime.date(2022, 2, 23),
datetime.date(2022, 2, 28),
]
]
assert slots['events'] == [
event2,
@ -2127,23 +2129,27 @@ def test_events_timesheet_slots(app, admin_user):
'events': [
{
'event': event2,
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 1)},
'dates': {date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 1)},
},
{
'event': recurring_event1,
'dates': {date: False for date in slots['dates'] if date.weekday() in [0, 1]},
'dates': {date: False for date in slots['dates'][0] if date.weekday() in [0, 1]},
},
{
'event': recurring_event2,
'dates': {date: False for date in slots['dates'] if date.weekday() in [1, 2]},
'dates': {date: False for date in slots['dates'][0] if date.weekday() in [1, 2]},
},
{
'event': event3,
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 15)},
'dates': {
date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 15)
},
},
{
'event': event4,
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 28)},
'dates': {
date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 28)
},
},
],
},
@ -2195,9 +2201,11 @@ def test_events_timesheet_subscription_limits(app, admin_user):
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 28),
[
datetime.date(2022, 2, 1),
datetime.date(2022, 2, 15),
datetime.date(2022, 2, 28),
]
]
assert slots['events'] == [
@ -2567,12 +2575,163 @@ def test_events_timesheet_extra_data(app, admin_user):
assert slots['users'][0]['extra_data']['baz'] == ''
@pytest.mark.freeze_time('2022-04-01')
def test_events_timesheet_date_display(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
recurring_event = Event.objects.create(
label='recurring 1',
start_datetime=make_aware(datetime.datetime(2022, 1, 1, 12, 0)),
places=10,
agenda=agenda,
recurrence_days=[0],
recurrence_end_date=datetime.date(2022, 4, 1),
)
recurring_event.create_all_recurrences()
Subscription.objects.create(
agenda=agenda,
user_external_id='user:1',
user_first_name='Subscription',
user_last_name='42',
date_start=datetime.date(2022, 1, 1),
date_end=datetime.date(2022, 4, 1),
)
login(app)
resp = app.get('/manage/agendas/%s/events/timesheet' % agenda.pk)
resp.form['date_start'] = '2022-01-01'
resp.form['date_end'] = '2022-03-31'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
[
datetime.date(2022, 1, 3),
datetime.date(2022, 1, 10),
datetime.date(2022, 1, 17),
datetime.date(2022, 1, 24),
datetime.date(2022, 1, 31),
datetime.date(2022, 2, 7),
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 28),
datetime.date(2022, 3, 7),
datetime.date(2022, 3, 14),
datetime.date(2022, 3, 21),
datetime.date(2022, 3, 28),
]
]
resp.form['date_display'] = 'month'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
[
datetime.date(2022, 1, 3),
datetime.date(2022, 1, 10),
datetime.date(2022, 1, 17),
datetime.date(2022, 1, 24),
datetime.date(2022, 1, 31),
],
[
datetime.date(2022, 2, 7),
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 28),
],
[
datetime.date(2022, 3, 7),
datetime.date(2022, 3, 14),
datetime.date(2022, 3, 21),
datetime.date(2022, 3, 28),
],
]
resp.form['date_display'] = 'week'
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
[datetime.date(2022, 1, 3)],
[datetime.date(2022, 1, 10)],
[datetime.date(2022, 1, 17)],
[datetime.date(2022, 1, 24)],
[datetime.date(2022, 1, 31)],
[datetime.date(2022, 2, 7)],
[datetime.date(2022, 2, 14)],
[datetime.date(2022, 2, 21)],
[datetime.date(2022, 2, 28)],
[datetime.date(2022, 3, 7)],
[datetime.date(2022, 3, 14)],
[datetime.date(2022, 3, 21)],
[datetime.date(2022, 3, 28)],
]
resp.form['date_display'] = 'custom'
resp = resp.form.submit()
assert resp.context['form'].errors['custom_nb_dates_per_page'] == ['This field is required.']
resp.form['custom_nb_dates_per_page'] = 10
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
[
datetime.date(2022, 1, 3),
datetime.date(2022, 1, 10),
datetime.date(2022, 1, 17),
datetime.date(2022, 1, 24),
datetime.date(2022, 1, 31),
datetime.date(2022, 2, 7),
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 28),
datetime.date(2022, 3, 7),
],
[
datetime.date(2022, 3, 14),
datetime.date(2022, 3, 21),
datetime.date(2022, 3, 28),
],
]
resp.form['custom_nb_dates_per_page'] = 3
resp = resp.form.submit()
slots = resp.context['form'].get_slots()
assert slots['dates'] == [
[
datetime.date(2022, 1, 3),
datetime.date(2022, 1, 10),
datetime.date(2022, 1, 17),
],
[
datetime.date(2022, 1, 24),
datetime.date(2022, 1, 31),
datetime.date(2022, 2, 7),
],
[
datetime.date(2022, 2, 14),
datetime.date(2022, 2, 21),
datetime.date(2022, 2, 28),
],
[
datetime.date(2022, 3, 7),
datetime.date(2022, 3, 14),
datetime.date(2022, 3, 21),
],
[
datetime.date(2022, 3, 28),
],
]
def test_events_timesheet_pdf(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
login(app)
resp = app.get(
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&orientation=portrait'
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&date_display=all&orientation=portrait'
% agenda.pk
)
assert resp.headers['Content-Type'] == 'application/pdf'
@ -2583,7 +2742,7 @@ 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'
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&date_display=all'
% agenda.pk
)
assert resp.context['form'].errors['orientation'] == ['This field is required.']