Projet

Général

Profil

0003-backoffice-formdata-display-fields-in-block-field-46.patch

Lauréline Guérin, 18 janvier 2022 09:46

Télécharger (17,1 ko)

Voir les différences:

Subject: [PATCH 3/4] backoffice: formdata display fields in block field
 (#46500)

 tests/backoffice_pages/test_columns.py | 48 +++++++++++++++-
 tests/backoffice_pages/test_filters.py | 10 ++--
 wcs/backoffice/management.py           | 60 +++++++++-----------
 wcs/formdata.py                        | 78 ++++++++++++++------------
 wcs/forms/backoffice.py                |  7 ++-
 5 files changed, 123 insertions(+), 80 deletions(-)
tests/backoffice_pages/test_columns.py
423 423

  
424 424

  
425 425
def test_backoffice_block_columns(pub):
426
    if not pub.is_using_postgresql():
427
        pytest.skip('this requires SQL')
428
        return
429

  
426 430
    pub.user_class.wipe()
427 431
    create_superuser(pub)
428 432
    pub.role_class.wipe()
429 433
    role = pub.role_class(name='test')
430 434
    role.store()
431 435

  
436
    CardDef.wipe()
437
    carddef = CardDef()
438
    carddef.name = 'foo'
439
    carddef.fields = [
440
        fields.StringField(id='1', label='First Name', type='string', varname='first_name'),
441
        fields.StringField(id='2', label='Last Name', type='string', varname='last_name'),
442
    ]
443
    carddef.digest_templates = {'default': '{{ form_var_first_name }} {{ form_var_last_name }}'}
444
    carddef.store()
445
    carddef.data_class().wipe()
446
    card = carddef.data_class()()
447
    card.data = {
448
        '1': 'Foo',
449
        '2': 'Bar',
450
    }
451
    card.store()
452

  
432 453
    BlockDef.wipe()
433 454
    block = BlockDef()
434 455
    block.name = 'foobar'
435 456
    block.fields = [
436 457
        fields.StringField(id='123', required=True, label='Test', type='string'),
458
        fields.ItemField(id='456', label='card field', type='item', data_source={'type': 'carddef:foo'}),
437 459
    ]
438 460
    block.store()
439 461

  
......
446 468
    formdef.workflow_roles = {'_receiver': role.id}
447 469
    formdef.store()
448 470

  
471
    data_class = formdef.data_class()
472
    data_class.wipe()
473

  
474
    formdata = data_class()
475
    formdata.data = {
476
        '8': {
477
            'data': [{'123': 'blah', '456': card.id, '456_display': card.default_digest}],
478
            'schema': {},  # not important here
479
        },
480
        '8_display': 'blah',
481
    }
482
    formdata.just_created()
483
    formdata.jump_status('new')
484
    formdata.store()
485

  
449 486
    app = login(get_app(pub))
450 487
    resp = app.get('/backoffice/management/form-title/')
451
    # check Block / Test is not part of the possible columns
452 488
    assert [x.text_content() for x in resp.pyquery('#columns-filter label')] == [
453 489
        'Number',
454 490
        'Created',
......
456 492
        'User Label',
457 493
        'Status',
458 494
        'Block',
495
        'Block / Test',
496
        'Block / card field',
459 497
        'Anonymised',
460 498
    ]
499
    resp.forms['listing-settings']['8-123'].checked = True
500
    resp.forms['listing-settings']['8-456'].checked = True
501
    resp = resp.forms['listing-settings'].submit()
502
    assert '<th><span>Block / Test</span></th>' in resp
503
    assert '<th><span>Block / card field</span></th>' in resp
504
    assert resp.text.count('<tr') == 2
505
    assert '<td>blah</td>' in resp
506
    assert '<td>Foo Bar</td>' in resp
tests/backoffice_pages/test_filters.py
980 980

  
981 981
    app = login(get_app(pub))
982 982
    resp = app.get('/backoffice/management/form-title/')
983
    assert '<label><input type="checkbox" name="1"/>Block Data / String</label>' not in resp
984
    assert '<label><input type="checkbox" name="2"/>Block Data / Item</label>' not in resp
985
    assert '<label><input type="checkbox" name="3"/>Block Data / Bool</label>' not in resp
986
    assert '<label><input type="checkbox" name="4"/>Block Data / Date</label>' not in resp
987
    assert '<label><input type="checkbox" name="5"/>Block Data / Email</label>' not in resp
983
    assert '<label><input type="checkbox" name="0-1"/>Block Data / String</label>' in resp
984
    assert '<label><input type="checkbox" name="0-2"/>Block Data / Item</label>' in resp
985
    assert '<label><input type="checkbox" name="0-3"/>Block Data / Bool</label>' in resp
986
    assert '<label><input type="checkbox" name="0-4"/>Block Data / Date</label>' in resp
987
    assert '<label><input type="checkbox" name="0-5"/>Block Data / Email</label>' in resp
988 988

  
989 989
    # string
990 990
    resp = app.get('/backoffice/management/form-title/')
wcs/backoffice/management.py
993 993
        get_request().is_json_marker = True
994 994
        field_id = get_request().form.get('filter_field_id')
995 995
        for filter_field in self.get_formdef_fields():
996
            is_in_block_field = getattr(filter_field, 'block_field', None)
997
            filter_field.contextual_id = filter_field.id
998
            if is_in_block_field:
999
                filter_field.contextual_id = '%s-%s' % (filter_field.block_field.id, filter_field.id)
1000 996
            if filter_field.contextual_id == field_id:
1001 997
                break
1002 998
        else:
......
1054 1050
                continue
1055 1051
            filter_fields.append(field)
1056 1052

  
1057
            # add contextual_id/contextual_varname attributes
1058
            # they are id/varname for normal fields
1059
            # but in case of blocks they are concatenation of block id/varname + field id/varname
1060
            is_in_block_field = getattr(field, 'block_field', None)
1061
            field.contextual_id = field.id
1062
            field.contextual_varname = None
1063
            if is_in_block_field:
1064
                field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
1053
            if getattr(field, 'block_field', None):
1065 1054
                field.label = '%s / %s' % (field.block_field.label, field.label)
1066
                if field.varname and field.block_field.varname:
1067
                    field.contextual_varname = '%s_%s' % (field.block_field.varname, field.varname)
1068
            else:
1069
                field.contextual_varname = field.varname
1070 1055

  
1071 1056
            if get_request().form:
1072 1057
                field.enabled = ('filter-%s' % field.contextual_id in get_request().form) or (
......
1351 1336
            for field in sorted(self.get_formdef_fields(), key=get_column_position):
1352 1337
                if not hasattr(field, 'get_view_value'):
1353 1338
                    continue
1354
                if getattr(field, 'block_field', None):
1355
                    # fields from blocks cannot yet be added as column
1356
                    continue
1357 1339
                classnames = ''
1358 1340
                attrs = ''
1359 1341
                if isinstance(field, RelatedField):
......
1366 1348
                    attrs = 'data-field-id="%s"' % field.id
1367 1349
                    seen_parents.add(field.id)
1368 1350
                r += htmltext('<li class="%s" %s><span class="handle">⣿</span>' % (classnames, attrs))
1369
                r += htmltext('<label><input type="checkbox" name="%s"') % field.id
1351
                r += htmltext('<label><input type="checkbox" name="%s"') % field.contextual_id
1370 1352
                if field.id in field_ids:
1371 1353
                    r += htmltext(' checked="checked"')
1372 1354
                r += htmltext('/>')
......
1565 1547

  
1566 1548
        def iter_fields(fields, block_field=None):
1567 1549
            for field in fields:
1550
                # add contextual_id/contextual_varname attributes
1551
                # they are id/varname for normal fields
1552
                # but in case of blocks they are concatenation of block id/varname + field id/varname
1553
                field.contextual_id = field.id
1554
                field.contextual_varname = None
1568 1555
                if block_field:
1569 1556
                    if field.key == 'items':
1570 1557
                        # not yet
1571 1558
                        continue
1572 1559
                    field.block_field = block_field
1560
                    field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
1561
                    if field.varname and field.block_field.varname:
1562
                        field.contextual_varname = '%s_%s' % (
1563
                            field.block_field.varname,
1564
                            field.varname,
1565
                        )
1566
                else:
1567
                    field.contextual_varname = field.varname
1573 1568
                yield field
1574 1569
                if not get_publisher().is_using_postgresql():
1575 1570
                    continue
1576 1571
                if field.key == 'block':
1577 1572
                    yield from iter_fields(field.block.fields, block_field=field)
1578 1573
                    continue
1574
                if block_field:
1575
                    continue
1579 1576
                if not (
1580 1577
                    field.type == 'item'
1581 1578
                    and field.data_source
......
1615 1612

  
1616 1613
        fields = []
1617 1614
        for field in self.get_formdef_fields():
1618
            if field.id in field_ids:
1615
            if field.contextual_id in field_ids:
1619 1616
                fields.append(field)
1620 1617

  
1621 1618
        if 'columns-order' in get_request().form or self.view:
......
1681 1678

  
1682 1679
            filter_field_key = None
1683 1680

  
1684
            is_in_block_field = getattr(filter_field, 'block_field', None)
1685
            filter_field.contextual_id = filter_field.id
1686
            filter_field.contextual_varname = None
1687
            if is_in_block_field:
1688
                filter_field.contextual_id = '%s-%s' % (filter_field.block_field.id, filter_field.id)
1689
                if filter_field.varname and filter_field.block_field.varname:
1690
                    filter_field.contextual_varname = '%s_%s' % (
1691
                        filter_field.block_field.varname,
1692
                        filter_field.varname,
1693
                    )
1694
            else:
1695
                filter_field.contextual_varname = filter_field.varname
1696

  
1697 1681
            if filter_field.contextual_varname:
1698 1682
                # if this is a field with a varname and filter-%(varname)s is
1699 1683
                # present in the query string, enable this filter.
......
1771 1755
                else:
1772 1756
                    raise RequestError('Invalid value "%s" for "%s"' % (filter_field_value, filter_field_key))
1773 1757

  
1774
            if is_in_block_field:
1758
            if getattr(filter_field, 'block_field', None):
1775 1759
                criterias.append(
1776 1760
                    sql.ArrayContains(
1777 1761
                        'f%s' % filter_field.block_field.id,
......
3433 3417
class FakeField:
3434 3418
    def __init__(self, id, type_, label, addable=True):
3435 3419
        self.id = id
3420
        self.contextual_id = self.id
3436 3421
        self.type = type_
3437 3422
        self.label = force_text(label)
3438 3423
        self.fake = True
3439 3424
        self.varname = id.replace('-', '_')
3425
        self.contextual_varname = self.varname
3440 3426
        self.store_display_value = None
3441 3427
        self.addable = addable
3442 3428

  
......
3471 3457
    def id(self):
3472 3458
        return '%s$%s' % (self.parent_field_id, self.related_field.id)
3473 3459

  
3460
    @property
3461
    def contextual_id(self):
3462
        return self.id
3463

  
3474 3464
    @property
3475 3465
    def label(self):
3476 3466
        return '%s - %s' % (self.parent_field.label, self.related_field.label)
wcs/formdata.py
797 797
            return None
798 798

  
799 799
    def get_field_view_value(self, field, max_length=None):
800
        # return the value of the given field, with special handling for "fake"
801
        # field types that are shortcuts to internal properties.
802
        if field.type == 'id':
803
            return self.get_display_id()
804
        if field.type == 'display_name':
805
            return self.get_display_name()
806
        if field.type == 'time':
807
            return misc.localstrftime(self.receipt_time)
808
        if field.type == 'last_update_time':
809
            return misc.localstrftime(self.last_update_time)
810
        if field.type == 'user-label':
811
            return self.get_user_label() or '-'
812
        if field.type == 'status':
813
            return self.get_status_label()
814
        if field.type == 'submission_channel':
815
            return self.get_submission_channel_label()
816
        if field.type == 'submission_agent':
817
            try:
818
                agent_user = self.submission_agent_id
819
                return get_publisher().user_class.get(agent_user).display_name
820
            except (KeyError, TypeError):
821
                return '-'
822
        if field.type == 'anonymised':
823
            return _('Yes') if self.anonymised else _('No')
824

  
825
        field_value = self.data.get(field.id)
826
        if field_value is None:
827
            return ''
828
        if max_length is not None:
829
            # if max_length is set the target is a backoffice listing/table,
830
            # return an html value, appropriately shortened.
831
            field_value = self.data.get('%s_display' % field.id, field_value)
832
            return field.get_view_short_value(field_value, max_length)
833
        else:
834
            # otherwise return the actual "raw" field value
835
            return field_value
800
        def get_value(field, data):
801
            # return the value of the given field, with special handling for "fake"
802
            # field types that are shortcuts to internal properties.
803
            if field.type == 'id':
804
                return self.get_display_id()
805
            if field.type == 'display_name':
806
                return self.get_display_name()
807
            if field.type == 'time':
808
                return misc.localstrftime(self.receipt_time)
809
            if field.type == 'last_update_time':
810
                return misc.localstrftime(self.last_update_time)
811
            if field.type == 'user-label':
812
                return self.get_user_label() or '-'
813
            if field.type == 'status':
814
                return self.get_status_label()
815
            if field.type == 'submission_channel':
816
                return self.get_submission_channel_label()
817
            if field.type == 'submission_agent':
818
                try:
819
                    agent_user = self.submission_agent_id
820
                    return get_publisher().user_class.get(agent_user).display_name
821
                except (KeyError, TypeError):
822
                    return '-'
823
            if field.type == 'anonymised':
824
                return _('Yes') if self.anonymised else _('No')
825

  
826
            field_value = data.get(field.id)
827
            if field_value is None:
828
                return ''
829
            if max_length is not None:
830
                # if max_length is set the target is a backoffice listing/table,
831
                # return an html value, appropriately shortened.
832
                field_value = data.get('%s_display' % field.id, field_value)
833
                return field.get_view_short_value(field_value, max_length)
834
            else:
835
                # otherwise return the actual "raw" field value
836
                return field_value
837

  
838
        if getattr(field, 'block_field', None):
839
            data = self.data.get(field.block_field.id) or {}
840
            return ', '.join(get_value(field, d) for d in data.get('data') or [])
841
        return get_value(field, self.data)
836 842

  
837 843
    def update_workflow_data(self, dict):
838 844
        if not self.workflow_data:
wcs/forms/backoffice.py
105 105
                ' <span id="info-all-rows"><label><input type="checkbox" name="select[]" value="_all"/> %s</label></span></th>'
106 106
            ) % _('Run selected action on all pages')
107 107
        for f in fields:
108
            field_sort_key = None
109 108
            if getattr(f, 'fake', False):
110 109
                field_sort_key = f.id
111 110
                if f.id == 'time':
112 111
                    field_sort_key = 'receipt_time'
113 112
                elif f.id in ('user-label', 'submission_agent'):
114 113
                    field_sort_key = None
115
            elif getattr(f, 'is_related_field', False):
114
            elif getattr(f, 'is_related_field', False) or getattr(f, 'block_field', None):
116 115
                field_sort_key = None
117 116
            else:
118
                field_sort_key = 'f%s' % f.id
117
                field_sort_key = 'f%s' % f.contextual_id
119 118

  
120 119
            if field_sort_key and using_postgresql:
121 120
                r += htmltext('<th data-field-sort-key="%s">') % field_sort_key
122 121
            else:
123 122
                r += htmltext('<th>')
123
            if getattr(f, 'block_field', None):
124
                f.label = '%s / %s' % (f.block_field.label, f.label)
124 125
            if len(f.label) < 20:
125 126
                r += htmltext('<span>%s</span>') % f.label
126 127
            else:
127
-