Projet

Général

Profil

0005-manager-timesheet-and-date-display-61070.patch

Lauréline Guérin, 18 février 2022 15:32

Télécharger (15,2 ko)

Voir les différences:

Subject: [PATCH 5/5] manager: timesheet and date display (#61070)

 chrono/manager/forms.py                       |  34 +++
 chrono/manager/static/css/style.scss          |   4 +
 chrono/manager/static/css/timesheet.scss      |   5 +
 .../chrono/manager_events_timesheet.html      |  12 ++
 .../manager_events_timesheet_fragment.html    |   7 +-
 tests/manager/test_event.py                   | 203 ++++++++++++++++--
 6 files changed, 241 insertions(+), 24 deletions(-)
chrono/manager/forms.py
376 376
        required=False,
377 377
        help_text=_('Comma separated list of keys defined in extra_data.'),
378 378
    )
379
    date_display = forms.ChoiceField(
380
        label=_('Display'),
381
        choices=[
382
            ('all', _('All on the same page')),
383
            ('month', _('1 month per page')),
384
            ('week', _('1 week per page')),
385
            ('custom', _('Custom')),
386
        ],
387
        initial='all',
388
    )
389
    custom_nb_dates_per_page = forms.IntegerField(
390
        label=_('Number of dates per page'),
391
        required=False,
392
    )
379 393
    orientation = forms.ChoiceField(
380 394
        label=_('PDF orientation'),
381 395
        choices=[
......
421 435
            dates_per_event_id[real_event.pk].append(date)
422 436
        dates = sorted(dates)
423 437

  
438
        date_display = self.cleaned_data['date_display']
439
        if date_display in ['month', 'week']:
440
            grouper = defaultdict(list)
441
            for date in dates:
442
                if date_display == 'month':
443
                    attr = date.month
444
                else:
445
                    attr = date.isocalendar().week
446
                grouper[(date.year, attr)].append(date)
447
            dates = [grouper[g] for g in sorted(grouper.keys())]
448
        elif date_display == 'custom':
449
            n = self.cleaned_data['custom_nb_dates_per_page']
450
            dates = [dates[i : i + n] for i in range(0, len(dates), n)]
451
        else:
452
            dates = [dates]
453

  
424 454
        event_slots = []
425 455
        for event in events:
426 456
            event_slots.append(
......
493 523
            elif (cleaned_data['date_start'] + relativedelta(months=3)) < cleaned_data['date_end']:
494 524
                self.add_error('date_end', _('Please select an interval of no more than 3 months.'))
495 525

  
526
        if cleaned_data.get('date_display') == 'custom':
527
            if not cleaned_data.get('custom_nb_dates_per_page'):
528
                self.add_error('custom_nb_dates_per_page', _('This field is required.'))
529

  
496 530
        return cleaned_data
497 531

  
498 532

  
chrono/manager/static/css/style.scss
503 503
		}
504 504
	}
505 505
}
506

  
507
.page_break {
508
	height: 20px;
509
}
chrono/manager/static/css/timesheet.scss
36 36
		}
37 37
	}
38 38
}
39

  
40
.page_break {
41
	display: block;
42
	page-break-before: always;
43
}
chrono/manager/templates/chrono/manager_events_timesheet.html
14 14
  <div>
15 15
    <form id="timesheet">
16 16
      {{ form.as_p }}
17
      <script>
18
      $(function() {
19
        $('#id_date_display').on('change', function() {
20
          if ($(this).val() == 'custom') {
21
            $('#id_custom_nb_dates_per_page').parent().show();
22
          } else {
23
            $('#id_custom_nb_dates_per_page').parent().hide();
24
          }
25
        });
26
        $('#id_date_display').trigger('change');
27
      });
28
      </script>
17 29
      <button class="submit-button">{% trans "See timesheet" %}</button>
18 30
      {% if request.GET and form.is_valid %}
19 31
      <button class="submit-button" name="pdf">{% trans "Get PDF file" %}</button>
chrono/manager/templates/chrono/manager_events_timesheet_fragment.html
2 2

  
3 3
{% with slots=form.get_slots %}
4 4
{% with events_num=slots.events|length %}
5
{% for dates in slots.dates %}
5 6
<table class="main timesheet">
6 7
  <thead>
7 8
    <tr>
......
9 10
      <th>{% trans "Last name" %}</th>
10 11
      {% for k in slots.extra_data %}<th>{{ k }}</th>{% endfor %}
11 12
      {% if events_num > 1 %}<th>{% trans "Activity" %}</th>{% endif %}
12
      {% for date in slots.dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
13
      {% for date in dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
13 14
    </tr>
14 15
  </thead>
15 16
  <tbody>
......
21 22
      {% for k in slots.extra_data %}<td {% if events_num > 1 %}rowspan="{{ events_num }}"{% endif %}>{{ user.extra_data|get:k }}</td>{% endfor %}
22 23
      {% endif %}
23 24
      {% if events_num > 1 %}<td>{{ event.event }}</td>{% endif %}
24
      {% for date in slots.dates %}
25
      {% for date in dates %}
25 26
      {% with booked=event.dates|get:date %}<td class="date">{% if booked is True %}☐{% elif booked is None %}-{% endif %}</td>{% endwith %}
26 27
      {% endfor %}
27 28
    </tr>
28 29
    {% endfor %}{% endfor %}
29 30
  </tbody>
30 31
</table>
32
{% if not forloop.last %}<div class="page_break"></div>{% endif %}
33
{% endfor %}
31 34
{% endwith %}
32 35
{% endwith %}
tests/manager/test_event.py
2098 2098

  
2099 2099
    slots = resp.context['form'].get_slots()
2100 2100
    assert slots['dates'] == [
2101
        datetime.date(2022, 2, 1),
2102
        datetime.date(2022, 2, 2),
2103
        datetime.date(2022, 2, 7),
2104
        datetime.date(2022, 2, 8),
2105
        datetime.date(2022, 2, 9),
2106
        datetime.date(2022, 2, 14),
2107
        datetime.date(2022, 2, 15),
2108
        datetime.date(2022, 2, 16),
2109
        datetime.date(2022, 2, 21),
2110
        datetime.date(2022, 2, 22),
2111
        datetime.date(2022, 2, 23),
2112
        datetime.date(2022, 2, 28),
2101
        [
2102
            datetime.date(2022, 2, 1),
2103
            datetime.date(2022, 2, 2),
2104
            datetime.date(2022, 2, 7),
2105
            datetime.date(2022, 2, 8),
2106
            datetime.date(2022, 2, 9),
2107
            datetime.date(2022, 2, 14),
2108
            datetime.date(2022, 2, 15),
2109
            datetime.date(2022, 2, 16),
2110
            datetime.date(2022, 2, 21),
2111
            datetime.date(2022, 2, 22),
2112
            datetime.date(2022, 2, 23),
2113
            datetime.date(2022, 2, 28),
2114
        ]
2113 2115
    ]
2114 2116
    assert slots['events'] == [
2115 2117
        event2,
......
2127 2129
            'events': [
2128 2130
                {
2129 2131
                    'event': event2,
2130
                    'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 1)},
2132
                    'dates': {date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 1)},
2131 2133
                },
2132 2134
                {
2133 2135
                    'event': recurring_event1,
2134
                    'dates': {date: False for date in slots['dates'] if date.weekday() in [0, 1]},
2136
                    'dates': {date: False for date in slots['dates'][0] if date.weekday() in [0, 1]},
2135 2137
                },
2136 2138
                {
2137 2139
                    'event': recurring_event2,
2138
                    'dates': {date: False for date in slots['dates'] if date.weekday() in [1, 2]},
2140
                    'dates': {date: False for date in slots['dates'][0] if date.weekday() in [1, 2]},
2139 2141
                },
2140 2142
                {
2141 2143
                    'event': event3,
2142
                    'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 15)},
2144
                    'dates': {
2145
                        date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 15)
2146
                    },
2143 2147
                },
2144 2148
                {
2145 2149
                    'event': event4,
2146
                    'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 28)},
2150
                    'dates': {
2151
                        date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 28)
2152
                    },
2147 2153
                },
2148 2154
            ],
2149 2155
        },
......
2195 2201

  
2196 2202
    slots = resp.context['form'].get_slots()
2197 2203
    assert slots['dates'] == [
2198
        datetime.date(2022, 2, 1),
2199
        datetime.date(2022, 2, 15),
2200
        datetime.date(2022, 2, 28),
2204
        [
2205
            datetime.date(2022, 2, 1),
2206
            datetime.date(2022, 2, 15),
2207
            datetime.date(2022, 2, 28),
2208
        ]
2201 2209
    ]
2202 2210

  
2203 2211
    assert slots['events'] == [
......
2567 2575
    assert slots['users'][0]['extra_data']['baz'] == ''
2568 2576

  
2569 2577

  
2578
@pytest.mark.freeze_time('2022-04-01')
2579
def test_events_timesheet_date_display(app, admin_user):
2580
    agenda = Agenda.objects.create(label='Events', kind='events')
2581
    recurring_event = Event.objects.create(
2582
        label='recurring 1',
2583
        start_datetime=make_aware(datetime.datetime(2022, 1, 1, 12, 0)),
2584
        places=10,
2585
        agenda=agenda,
2586
        recurrence_days=[0],
2587
        recurrence_end_date=datetime.date(2022, 4, 1),
2588
    )
2589
    recurring_event.create_all_recurrences()
2590
    Subscription.objects.create(
2591
        agenda=agenda,
2592
        user_external_id='user:1',
2593
        user_first_name='Subscription',
2594
        user_last_name='42',
2595
        date_start=datetime.date(2022, 1, 1),
2596
        date_end=datetime.date(2022, 4, 1),
2597
    )
2598

  
2599
    login(app)
2600
    resp = app.get('/manage/agendas/%s/events/timesheet' % agenda.pk)
2601
    resp.form['date_start'] = '2022-01-01'
2602
    resp.form['date_end'] = '2022-03-31'
2603
    resp = resp.form.submit()
2604
    slots = resp.context['form'].get_slots()
2605

  
2606
    assert slots['dates'] == [
2607
        [
2608
            datetime.date(2022, 1, 3),
2609
            datetime.date(2022, 1, 10),
2610
            datetime.date(2022, 1, 17),
2611
            datetime.date(2022, 1, 24),
2612
            datetime.date(2022, 1, 31),
2613
            datetime.date(2022, 2, 7),
2614
            datetime.date(2022, 2, 14),
2615
            datetime.date(2022, 2, 21),
2616
            datetime.date(2022, 2, 28),
2617
            datetime.date(2022, 3, 7),
2618
            datetime.date(2022, 3, 14),
2619
            datetime.date(2022, 3, 21),
2620
            datetime.date(2022, 3, 28),
2621
        ]
2622
    ]
2623

  
2624
    resp.form['date_display'] = 'month'
2625
    resp = resp.form.submit()
2626
    slots = resp.context['form'].get_slots()
2627

  
2628
    assert slots['dates'] == [
2629
        [
2630
            datetime.date(2022, 1, 3),
2631
            datetime.date(2022, 1, 10),
2632
            datetime.date(2022, 1, 17),
2633
            datetime.date(2022, 1, 24),
2634
            datetime.date(2022, 1, 31),
2635
        ],
2636
        [
2637
            datetime.date(2022, 2, 7),
2638
            datetime.date(2022, 2, 14),
2639
            datetime.date(2022, 2, 21),
2640
            datetime.date(2022, 2, 28),
2641
        ],
2642
        [
2643
            datetime.date(2022, 3, 7),
2644
            datetime.date(2022, 3, 14),
2645
            datetime.date(2022, 3, 21),
2646
            datetime.date(2022, 3, 28),
2647
        ],
2648
    ]
2649

  
2650
    resp.form['date_display'] = 'week'
2651
    resp = resp.form.submit()
2652
    slots = resp.context['form'].get_slots()
2653

  
2654
    assert slots['dates'] == [
2655
        [datetime.date(2022, 1, 3)],
2656
        [datetime.date(2022, 1, 10)],
2657
        [datetime.date(2022, 1, 17)],
2658
        [datetime.date(2022, 1, 24)],
2659
        [datetime.date(2022, 1, 31)],
2660
        [datetime.date(2022, 2, 7)],
2661
        [datetime.date(2022, 2, 14)],
2662
        [datetime.date(2022, 2, 21)],
2663
        [datetime.date(2022, 2, 28)],
2664
        [datetime.date(2022, 3, 7)],
2665
        [datetime.date(2022, 3, 14)],
2666
        [datetime.date(2022, 3, 21)],
2667
        [datetime.date(2022, 3, 28)],
2668
    ]
2669

  
2670
    resp.form['date_display'] = 'custom'
2671
    resp = resp.form.submit()
2672
    assert resp.context['form'].errors['custom_nb_dates_per_page'] == ['This field is required.']
2673

  
2674
    resp.form['custom_nb_dates_per_page'] = 10
2675
    resp = resp.form.submit()
2676
    slots = resp.context['form'].get_slots()
2677

  
2678
    assert slots['dates'] == [
2679
        [
2680
            datetime.date(2022, 1, 3),
2681
            datetime.date(2022, 1, 10),
2682
            datetime.date(2022, 1, 17),
2683
            datetime.date(2022, 1, 24),
2684
            datetime.date(2022, 1, 31),
2685
            datetime.date(2022, 2, 7),
2686
            datetime.date(2022, 2, 14),
2687
            datetime.date(2022, 2, 21),
2688
            datetime.date(2022, 2, 28),
2689
            datetime.date(2022, 3, 7),
2690
        ],
2691
        [
2692
            datetime.date(2022, 3, 14),
2693
            datetime.date(2022, 3, 21),
2694
            datetime.date(2022, 3, 28),
2695
        ],
2696
    ]
2697

  
2698
    resp.form['custom_nb_dates_per_page'] = 3
2699
    resp = resp.form.submit()
2700
    slots = resp.context['form'].get_slots()
2701

  
2702
    assert slots['dates'] == [
2703
        [
2704
            datetime.date(2022, 1, 3),
2705
            datetime.date(2022, 1, 10),
2706
            datetime.date(2022, 1, 17),
2707
        ],
2708
        [
2709
            datetime.date(2022, 1, 24),
2710
            datetime.date(2022, 1, 31),
2711
            datetime.date(2022, 2, 7),
2712
        ],
2713
        [
2714
            datetime.date(2022, 2, 14),
2715
            datetime.date(2022, 2, 21),
2716
            datetime.date(2022, 2, 28),
2717
        ],
2718
        [
2719
            datetime.date(2022, 3, 7),
2720
            datetime.date(2022, 3, 14),
2721
            datetime.date(2022, 3, 21),
2722
        ],
2723
        [
2724
            datetime.date(2022, 3, 28),
2725
        ],
2726
    ]
2727

  
2728

  
2570 2729
def test_events_timesheet_pdf(app, admin_user):
2571 2730
    agenda = Agenda.objects.create(label='Events', kind='events')
2572 2731

  
2573 2732
    login(app)
2574 2733
    resp = app.get(
2575
        '/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&extra_data=&orientation=portrait'
2734
        '/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&extra_data=&date_display=all&orientation=portrait'
2576 2735
        % agenda.pk
2577 2736
    )
2578 2737
    assert resp.headers['Content-Type'] == 'application/pdf'
......
2583 2742

  
2584 2743
    # form invalid
2585 2744
    resp = app.get(
2586
        '/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&extra_data='
2745
        '/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&extra_data=&date_display=all'
2587 2746
        % agenda.pk
2588 2747
    )
2589 2748
    assert resp.context['form'].errors['orientation'] == ['This field is required.']
2590
-