Projet

Général

Profil

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

Lauréline Guérin, 17 janvier 2022 14:40

Télécharger (17,1 ko)

Voir les différences:

Subject: [PATCH 3/3] 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
981 981
        get_request().is_json_marker = True
982 982
        field_id = get_request().form.get('filter_field_id')
983 983
        for filter_field in self.get_formdef_fields():
984
            is_in_block_field = getattr(filter_field, 'block_field', None)
985
            filter_field.contextual_id = filter_field.id
986
            if is_in_block_field:
987
                filter_field.contextual_id = '%s-%s' % (filter_field.block_field.id, filter_field.id)
988 984
            if filter_field.contextual_id == field_id:
989 985
                break
990 986
        else:
......
1042 1038
                continue
1043 1039
            filter_fields.append(field)
1044 1040

  
1045
            # add contextual_id/contextual_varname attributes
1046
            # they are id/varname for normal fields
1047
            # but in case of blocks they are concatenation of block id/varname + field id/varname
1048
            is_in_block_field = getattr(field, 'block_field', None)
1049
            field.contextual_id = field.id
1050
            field.contextual_varname = None
1051
            if is_in_block_field:
1052
                field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
1041
            if getattr(field, 'block_field', None):
1053 1042
                field.label = '%s / %s' % (field.block_field.label, field.label)
1054
                if field.varname and field.block_field.varname:
1055
                    field.contextual_varname = '%s_%s' % (field.block_field.varname, field.varname)
1056
            else:
1057
                field.contextual_varname = field.varname
1058 1043

  
1059 1044
            if get_request().form:
1060 1045
                field.enabled = ('filter-%s' % field.contextual_id in get_request().form) or (
......
1339 1324
            for field in sorted(self.get_formdef_fields(), key=get_column_position):
1340 1325
                if not hasattr(field, 'get_view_value'):
1341 1326
                    continue
1342
                if getattr(field, 'block_field', None):
1343
                    # fields from blocks cannot yet be added as column
1344
                    continue
1345 1327
                classnames = ''
1346 1328
                attrs = ''
1347 1329
                if isinstance(field, RelatedField):
......
1354 1336
                    attrs = 'data-field-id="%s"' % field.id
1355 1337
                    seen_parents.add(field.id)
1356 1338
                r += htmltext('<li class="%s" %s><span class="handle">⣿</span>' % (classnames, attrs))
1357
                r += htmltext('<label><input type="checkbox" name="%s"') % field.id
1339
                r += htmltext('<label><input type="checkbox" name="%s"') % field.contextual_id
1358 1340
                if field.id in field_ids:
1359 1341
                    r += htmltext(' checked="checked"')
1360 1342
                r += htmltext('/>')
......
1553 1535

  
1554 1536
        def iter_fields(fields, block_field=None):
1555 1537
            for field in fields:
1538
                # add contextual_id/contextual_varname attributes
1539
                # they are id/varname for normal fields
1540
                # but in case of blocks they are concatenation of block id/varname + field id/varname
1541
                field.contextual_id = field.id
1542
                field.contextual_varname = None
1556 1543
                if block_field:
1557 1544
                    if field.key == 'items':
1558 1545
                        # not yet
1559 1546
                        continue
1560 1547
                    field.block_field = block_field
1548
                    field.contextual_id = '%s-%s' % (field.block_field.id, field.id)
1549
                    if field.varname and field.block_field.varname:
1550
                        field.contextual_varname = '%s_%s' % (
1551
                            field.block_field.varname,
1552
                            field.varname,
1553
                        )
1554
                else:
1555
                    field.contextual_varname = field.varname
1561 1556
                yield field
1562 1557
                if not get_publisher().is_using_postgresql():
1563 1558
                    continue
1564 1559
                if field.key == 'block':
1565 1560
                    yield from iter_fields(field.block.fields, block_field=field)
1566 1561
                    continue
1562
                if block_field:
1563
                    continue
1567 1564
                if not (
1568 1565
                    field.type == 'item'
1569 1566
                    and field.data_source
......
1603 1600

  
1604 1601
        fields = []
1605 1602
        for field in self.get_formdef_fields():
1606
            if field.id in field_ids:
1603
            if field.contextual_id in field_ids:
1607 1604
                fields.append(field)
1608 1605

  
1609 1606
        if 'columns-order' in get_request().form or self.view:
......
1669 1666

  
1670 1667
            filter_field_key = None
1671 1668

  
1672
            is_in_block_field = getattr(filter_field, 'block_field', None)
1673
            filter_field.contextual_id = filter_field.id
1674
            filter_field.contextual_varname = None
1675
            if is_in_block_field:
1676
                filter_field.contextual_id = '%s-%s' % (filter_field.block_field.id, filter_field.id)
1677
                if filter_field.varname and filter_field.block_field.varname:
1678
                    filter_field.contextual_varname = '%s_%s' % (
1679
                        filter_field.block_field.varname,
1680
                        filter_field.varname,
1681
                    )
1682
            else:
1683
                filter_field.contextual_varname = filter_field.varname
1684

  
1685 1669
            if filter_field.contextual_varname:
1686 1670
                # if this is a field with a varname and filter-%(varname)s is
1687 1671
                # present in the query string, enable this filter.
......
1759 1743
                else:
1760 1744
                    raise RequestError('Invalid value "%s" for "%s"' % (filter_field_value, filter_field_key))
1761 1745

  
1762
            if is_in_block_field:
1746
            if getattr(filter_field, 'block_field', None):
1763 1747
                criterias.append(
1764 1748
                    sql.ArrayContains(
1765 1749
                        'f%s' % filter_field.block_field.id,
......
3421 3405
class FakeField:
3422 3406
    def __init__(self, id, type_, label, addable=True):
3423 3407
        self.id = id
3408
        self.contextual_id = self.id
3424 3409
        self.type = type_
3425 3410
        self.label = force_text(label)
3426 3411
        self.fake = True
3427 3412
        self.varname = id.replace('-', '_')
3413
        self.contextual_varname = self.varname
3428 3414
        self.store_display_value = None
3429 3415
        self.addable = addable
3430 3416

  
......
3459 3445
    def id(self):
3460 3446
        return '%s$%s' % (self.parent_field_id, self.related_field.id)
3461 3447

  
3448
    @property
3449
    def contextual_id(self):
3450
        return self.id
3451

  
3462 3452
    @property
3463 3453
    def label(self):
3464 3454
        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
-