Projet

Général

Profil

0006-manager-timesheet-grouper-61920.patch

Lauréline Guérin, 18 février 2022 16:55

Télécharger (15,6 ko)

Voir les différences:

Subject: [PATCH 6/6] manager: timesheet grouper (#61920)

 chrono/manager/forms.py                       |  42 +++++-
 .../chrono/manager_events_timesheet.html      |   8 ++
 .../manager_events_timesheet_fragment.html    |   8 +-
 tests/manager/test_event.py                   | 136 +++++++++++++-----
 4 files changed, 155 insertions(+), 39 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
    group_by = forms.CharField(
380
        label=_('Group by'),
381
        max_length=50,
382
        required=False,
383
        help_text=_('Key defined in extra_data.'),
384
    )
385
    with_page_break = forms.BooleanField(
386
        label=_('Add a page break before earch grouper'),
387
        required=False,
388
    )
379 389
    date_display = forms.ChoiceField(
380 390
        label=_('Display'),
381 391
        choices=[
......
406 416
    def get_slots(self):
407 417
        extra_data = self.cleaned_data['extra_data'].split(',')
408 418
        extra_data = [d.strip() for d in extra_data if d.strip()]
419
        group_by = self.cleaned_data['group_by'].strip()
420
        all_extra_data = extra_data[:]
421
        if group_by:
422
            all_extra_data += [group_by]
409 423
        min_start = make_aware(
410 424
            datetime.datetime.combine(self.cleaned_data['date_start'], datetime.time(0, 0))
411 425
        )
......
466 480
                'user_id': subscription.user_external_id,
467 481
                'user_first_name': subscription.user_first_name,
468 482
                'user_last_name': subscription.user_last_name,
469
                'extra_data': {k: (subscription.extra_data or {}).get(k) or '' for k in extra_data},
483
                'extra_data': {k: (subscription.extra_data or {}).get(k) or '' for k in all_extra_data},
470 484
                'events': copy.deepcopy(event_slots),
471 485
            }
472 486

  
......
491 505
                    'user_id': user_id,
492 506
                    'user_first_name': booking.user_first_name,
493 507
                    'user_last_name': booking.user_last_name,
494
                    'extra_data': {k: (booking.extra_data or {}).get(k) or '' for k in extra_data},
508
                    'extra_data': {k: (booking.extra_data or {}).get(k) or '' for k in all_extra_data},
495 509
                    'events': copy.deepcopy(event_slots),
496 510
                }
497 511
            if booking.cancellation_datetime is not None:
......
505 519
                    event['dates'][date] = True
506 520
                break
507 521

  
508
        users = sorted(users.values(), key=itemgetter('user_last_name', 'user_first_name', 'user_id'))
522
        if group_by:
523
            groupers = defaultdict(list)
524
            for user in users.values():
525
                groupers[user['extra_data'].get(group_by) or ''].append(user)
526
            users = [
527
                {
528
                    'grouper': g,
529
                    'users': sorted(u, key=itemgetter('user_last_name', 'user_first_name', 'user_id')),
530
                }
531
                for g, u in groupers.items()
532
            ]
533
            users = sorted(users, key=itemgetter('grouper'))
534
            if users and users[0]['grouper'] == '':
535
                users = users[1:] + users[:1]
536
        else:
537
            users = [
538
                {
539
                    'grouper': '',
540
                    'users': sorted(
541
                        users.values(), key=itemgetter('user_last_name', 'user_first_name', 'user_id')
542
                    ),
543
                }
544
            ]
509 545

  
510 546
        return {
511 547
            'dates': dates,
chrono/manager/templates/chrono/manager_events_timesheet.html
24 24
          }
25 25
        });
26 26
        $('#id_date_display').trigger('change');
27
        $('#id_group_by').on('change', function() {
28
          if ($(this).val()) {
29
            $('#id_with_page_break').parent().show();
30
          } else {
31
            $('#id_with_page_break').parent().hide();
32
          }
33
        });
34
        $('#id_group_by').trigger('change');
27 35
      });
28 36
      </script>
29 37
      <button class="submit-button">{% trans "See timesheet" %}</button>
chrono/manager/templates/chrono/manager_events_timesheet_fragment.html
3 3
{% with slots=form.get_slots %}
4 4
{% with events_num=slots.events|length %}
5 5
{% for dates in slots.dates %}
6
{% for grouper in slots.users %}
7
{% if form.cleaned_data.group_by %}<h5>{{ form.cleaned_data.group_by }}: {{ grouper.grouper }}</h5>{% endif %}
6 8
<table class="main timesheet">
7 9
  <thead>
8 10
    <tr>
......
14 16
    </tr>
15 17
  </thead>
16 18
  <tbody>
17
    {% for user in slots.users %}{% for event in user.events %}
19
    {% for user in grouper.users %}{% for event in user.events %}
18 20
    <tr>
19 21
      {% if forloop.first %}
20 22
      <td {% if events_num > 1 %}rowspan="{{ events_num }}"{% endif %}>{{ user.user_first_name }}</td>
......
29 31
    {% endfor %}{% endfor %}
30 32
  </tbody>
31 33
</table>
32
{% if not forloop.last %}<div class="page_break"></div>{% endif %}
34
{% if form.cleaned_data.with_page_break %}<div class="page_break"></div>{% endif %}
35
{% endfor %}
36
{% if not form.cleaned_data.with_page_break %}<div class="page_break"></div>{% endif %}
33 37
{% endfor %}
34 38
{% endwith %}
35 39
{% endwith %}
tests/manager/test_event.py
2120 2120
        event3,
2121 2121
        event4,
2122 2122
    ]
2123
    assert slots['users'] == [
2123
    assert slots['users'][0]['users'] == [
2124 2124
        {
2125 2125
            'user_id': 'user:1',
2126 2126
            'user_first_name': 'Subscription',
......
2213 2213
        event2,
2214 2214
        event3,
2215 2215
    ]
2216
    assert len(slots['users']) == 8
2217
    assert slots['users'][0]['user_id'] == 'user:2022-02-01-2022-02-02'
2218
    assert slots['users'][1]['user_id'] == 'user:2022-02-01-2022-02-15'
2219
    assert slots['users'][2]['user_id'] == 'user:2022-02-01-2022-02-16'
2220
    assert slots['users'][3]['user_id'] == 'user:2022-02-01-2022-03-01'
2221
    assert slots['users'][4]['user_id'] == 'user:2022-02-15-2022-02-28'
2222
    assert slots['users'][5]['user_id'] == 'user:2022-02-15-2022-03-01'
2223
    assert slots['users'][6]['user_id'] == 'user:2022-02-16-2022-03-01'
2224
    assert slots['users'][7]['user_id'] == 'user:2022-02-28-2022-03-01'
2216
    users = slots['users'][0]['users']
2217
    assert len(users) == 8
2218
    assert users[0]['user_id'] == 'user:2022-02-01-2022-02-02'
2219
    assert users[1]['user_id'] == 'user:2022-02-01-2022-02-15'
2220
    assert users[2]['user_id'] == 'user:2022-02-01-2022-02-16'
2221
    assert users[3]['user_id'] == 'user:2022-02-01-2022-03-01'
2222
    assert users[4]['user_id'] == 'user:2022-02-15-2022-02-28'
2223
    assert users[5]['user_id'] == 'user:2022-02-15-2022-03-01'
2224
    assert users[6]['user_id'] == 'user:2022-02-16-2022-03-01'
2225
    assert users[7]['user_id'] == 'user:2022-02-28-2022-03-01'
2225 2226

  
2226 2227

  
2227 2228
def test_events_timesheet_users(app, admin_user):
......
2278 2279
    resp.form['date_end'] = '2022-02-28'
2279 2280
    resp = resp.form.submit()
2280 2281
    slots = resp.context['form'].get_slots()
2281
    assert [u['user_id'] for u in slots['users']] == [
2282
    assert [u['user_id'] for u in slots['users'][0]['users']] == [
2282 2283
        'user:2',
2283 2284
        'user:5',
2284 2285
        'user:3',
......
2339 2340

  
2340 2341
    resp = resp.form.submit()
2341 2342
    slots = resp.context['form'].get_slots()
2342
    assert [u['user_id'] for u in slots['users']] == [
2343
    assert [u['user_id'] for u in slots['users'][0]['users']] == [
2343 2344
        'user:2',
2344 2345
        'user:5',
2345 2346
        'user:6',
......
2366 2367
    resp = resp.form.submit()
2367 2368
    slots = resp.context['form'].get_slots()
2368 2369
    # no user_id found
2369
    assert [u['user_id'] for u in slots['users']] == []
2370
    assert [u['user_first_name'] for u in slots['users']] == []
2371
    assert [u['user_last_name'] for u in slots['users']] == []
2370
    assert [u['user_id'] for u in slots['users'][0]['users']] == []
2371
    assert [u['user_first_name'] for u in slots['users'][0]['users']] == []
2372
    assert [u['user_last_name'] for u in slots['users'][0]['users']] == []
2372 2373

  
2373 2374
    booking.user_external_id = 'user:1'
2374 2375
    booking.save()
2375 2376

  
2376 2377
    resp = resp.form.submit()
2377 2378
    slots = resp.context['form'].get_slots()
2378
    assert [u['user_id'] for u in slots['users']] == [
2379
    assert [u['user_id'] for u in slots['users'][0]['users']] == [
2379 2380
        'user:1',
2380 2381
    ]
2381
    assert [u['user_first_name'] for u in slots['users']] == ['User']
2382
    assert [u['user_last_name'] for u in slots['users']] == ['42']
2382
    assert [u['user_first_name'] for u in slots['users'][0]['users']] == ['User']
2383
    assert [u['user_last_name'] for u in slots['users'][0]['users']] == ['42']
2383 2384

  
2384 2385
    Subscription.objects.create(
2385 2386
        agenda=agenda,
......
2392 2393

  
2393 2394
    resp = resp.form.submit()
2394 2395
    slots = resp.context['form'].get_slots()
2395
    assert [u['user_id'] for u in slots['users']] == [
2396
    assert [u['user_id'] for u in slots['users'][0]['users']] == [
2396 2397
        'user:1',
2397 2398
    ]
2398
    assert [u['user_first_name'] for u in slots['users']] == [
2399
    assert [u['user_first_name'] for u in slots['users'][0]['users']] == [
2399 2400
        'Subscription',
2400 2401
    ]
2401
    assert [u['user_last_name'] for u in slots['users']] == [
2402
    assert [u['user_last_name'] for u in slots['users'][0]['users']] == [
2402 2403
        '41',
2403 2404
    ]
2404 2405

  
......
2480 2481
        recurring_event1,
2481 2482
        recurring_event2,
2482 2483
    ]
2483
    assert len(slots['users']) == 1
2484
    assert slots['users'][0]['events'] == [
2484
    assert len(slots['users'][0]['users']) == 1
2485
    assert slots['users'][0]['users'][0]['events'] == [
2485 2486
        {
2486 2487
            'event': event1,
2487 2488
            'dates': {datetime.date(2022, 2, 15): True},
......
2527 2528
    slots = resp.context['form'].get_slots()
2528 2529

  
2529 2530
    assert len(slots['users']) == 1
2531
    assert slots['users'][0]['grouper'] == ''
2532
    assert len(slots['users'][0]['users']) == 1
2530 2533
    assert slots['extra_data'] == ['foo']
2531
    assert slots['users'][0]['extra_data']['foo'] == 'bar'
2534
    assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'bar'
2532 2535

  
2533 2536
    resp.form['extra_data'] = ' foo ,baz,,'
2534 2537
    resp = resp.form.submit()
2535 2538
    slots = resp.context['form'].get_slots()
2536 2539

  
2537
    assert len(slots['users']) == 1
2540
    assert len(slots['users'][0]['users']) == 1
2538 2541
    assert slots['extra_data'] == ['foo', 'baz']
2539
    assert slots['users'][0]['extra_data']['foo'] == 'bar'
2540
    assert slots['users'][0]['extra_data']['baz'] == 'blah'
2542
    assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'bar'
2543
    assert slots['users'][0]['users'][0]['extra_data']['baz'] == 'blah'
2541 2544

  
2542 2545
    resp.form['extra_data'] = 'unknown'
2543 2546
    resp = resp.form.submit()
2544 2547
    slots = resp.context['form'].get_slots()
2545 2548

  
2546
    assert len(slots['users']) == 1
2549
    assert len(slots['users'][0]['users']) == 1
2547 2550
    assert slots['extra_data'] == ['unknown']
2548
    assert slots['users'][0]['extra_data']['unknown'] == ''
2551
    assert slots['users'][0]['users'][0]['extra_data']['unknown'] == ''
2549 2552

  
2550 2553
    Subscription.objects.create(
2551 2554
        agenda=agenda,
......
2561 2564
    resp = resp.form.submit()
2562 2565
    slots = resp.context['form'].get_slots()
2563 2566

  
2564
    assert len(slots['users']) == 1
2567
    assert len(slots['users'][0]['users']) == 1
2565 2568
    assert slots['extra_data'] == ['foo']
2566
    assert slots['users'][0]['extra_data']['foo'] == 'baz'
2569
    assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'baz'
2567 2570

  
2568 2571
    resp.form['extra_data'] = ' foo ,baz,,'
2569 2572
    resp = resp.form.submit()
2570 2573
    slots = resp.context['form'].get_slots()
2571 2574

  
2572
    assert len(slots['users']) == 1
2575
    assert len(slots['users'][0]['users']) == 1
2573 2576
    assert slots['extra_data'] == ['foo', 'baz']
2574
    assert slots['users'][0]['extra_data']['foo'] == 'baz'
2575
    assert slots['users'][0]['extra_data']['baz'] == ''
2577
    assert slots['users'][0]['users'][0]['extra_data']['foo'] == 'baz'
2578
    assert slots['users'][0]['users'][0]['extra_data']['baz'] == ''
2579

  
2580
    Booking.objects.create(
2581
        event=event,
2582
        user_first_name='User',
2583
        user_last_name='43',
2584
        user_external_id='user:2',
2585
        extra_data={'foo': 'bar', 'baz': 'aa'},
2586
    )
2587
    Booking.objects.create(
2588
        event=event,
2589
        user_first_name='User',
2590
        user_last_name='44',
2591
        user_external_id='user:3',
2592
        extra_data={'foo': 'bar2', 'baz': 'aa'},
2593
    )
2594

  
2595
    resp.form['extra_data'] = ''
2596
    resp.form['group_by'] = 'foo'
2597
    resp = resp.form.submit()
2598
    slots = resp.context['form'].get_slots()
2599

  
2600
    assert len(slots['users']) == 3
2601
    assert slots['users'][0]['grouper'] == 'bar'
2602
    assert len(slots['users'][0]['users']) == 1
2603
    assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
2604
    assert slots['users'][1]['grouper'] == 'bar2'
2605
    assert len(slots['users'][1]['users']) == 1
2606
    assert slots['users'][1]['users'][0]['user_id'] == 'user:3'
2607
    assert slots['users'][2]['grouper'] == 'baz'
2608
    assert len(slots['users'][2]['users']) == 1
2609
    assert slots['users'][2]['users'][0]['user_id'] == 'user:1'
2610

  
2611
    resp.form['group_by'] = 'baz'
2612
    resp = resp.form.submit()
2613
    slots = resp.context['form'].get_slots()
2614

  
2615
    assert len(slots['users']) == 2
2616
    assert slots['users'][0]['grouper'] == 'aa'
2617
    assert len(slots['users'][0]['users']) == 2
2618
    assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
2619
    assert slots['users'][0]['users'][1]['user_id'] == 'user:3'
2620
    assert slots['users'][1]['grouper'] == ''
2621
    assert len(slots['users'][1]['users']) == 1
2622
    assert slots['users'][1]['users'][0]['user_id'] == 'user:1'
2623

  
2624
    Subscription.objects.update(extra_data={'foo': 'baz', 'baz': 'blah'})
2625
    resp = resp.form.submit()
2626
    slots = resp.context['form'].get_slots()
2627

  
2628
    assert len(slots['users']) == 2
2629
    assert slots['users'][0]['grouper'] == 'aa'
2630
    assert len(slots['users'][0]['users']) == 2
2631
    assert slots['users'][0]['users'][0]['user_id'] == 'user:2'
2632
    assert slots['users'][0]['users'][1]['user_id'] == 'user:3'
2633
    assert slots['users'][1]['grouper'] == 'blah'
2634
    assert len(slots['users'][1]['users']) == 1
2635
    assert slots['users'][1]['users'][0]['user_id'] == 'user:1'
2636

  
2637
    resp.form['group_by'] = 'unknown'
2638
    resp = resp.form.submit()
2639
    slots = resp.context['form'].get_slots()
2640

  
2641
    assert len(slots['users']) == 1
2642
    assert slots['users'][0]['grouper'] == ''
2643
    assert len(slots['users'][0]['users']) == 3
2576 2644

  
2577 2645

  
2578 2646
@pytest.mark.freeze_time('2022-04-01')
2579
-