Projet

Général

Profil

0001-misc-add-dynamic-autocomplete-for-select-of-cards-47.patch

Frédéric Péters, 30 septembre 2020 09:30

Télécharger (7,3 ko)

Voir les différences:

Subject: [PATCH] misc: add dynamic autocomplete for <select> of cards (#47152)

 tests/test_form_pages.py | 43 ++++++++++++++++++++++++++++++++++++++++
 wcs/api.py               | 13 +++++++++---
 wcs/carddef.py           |  9 ++++++---
 wcs/data_sources.py      |  7 ++++++-
 wcs/sessions.py          | 11 ++++++----
 5 files changed, 72 insertions(+), 11 deletions(-)
tests/test_form_pages.py
6091 6091
        assert '0_structured' not in formdef.data_class().select()[0].data
6092 6092

  
6093 6093

  
6094
def test_item_field_autocomplete_cards_source(pub):
6095
    user = create_user(pub)
6096
    formdef = create_formdef()
6097
    formdef.data_class().wipe()
6098

  
6099
    carddef = CardDef()
6100
    carddef.name = 'items'
6101
    carddef.digest_template = '{{form_var_name}}'
6102
    carddef.fields = [
6103
        fields.StringField(id='0', label='string', varname='name'),
6104
        fields.StringField(id='1', label='string', varname='attr'),
6105
    ]
6106
    carddef.store()
6107
    for i, value in enumerate(['foo', 'bar', 'baz']):
6108
        carddata = carddef.data_class()()
6109
        carddata.data = {
6110
            '0': value,
6111
            '1': 'attr%s' % i,
6112
        }
6113
        carddata.just_created()
6114
        carddata.store()
6115

  
6116
    ds = {'type': 'carddef:%s' % carddef.url_name}
6117
    formdef.fields = [
6118
        fields.ItemField(id='0', label='string', type='item',
6119
            data_source=ds,
6120
            display_mode='autocomplete',
6121
        ),
6122
    ]
6123
    formdef.store()
6124

  
6125
    app = get_app(pub)
6126
    resp = app.get('/test/')
6127
    select2_url = resp.pyquery('select').attr['data-select2-url']
6128
    resp2 = app.get(select2_url + '?q=ba')
6129
    assert [x['text'] for x in resp2.json['data']] == ['bar', 'baz']
6130
    resp.form['f0'].force_value(str(resp2.json['data'][0]['id']))
6131
    resp = resp.form.submit('submit')  # -> validation page
6132
    resp = resp.form.submit('submit')  # -> submit
6133
    assert formdef.data_class().select()[0].data['0'] == '2'
6134
    assert formdef.data_class().select()[0].data['0_display'] == 'bar'
6135

  
6136

  
6094 6137
def test_form_data_keywords(pub):
6095 6138
    formdef = create_formdef()
6096 6139
    formdef.keywords = 'hello,world'
wcs/api.py
829 829

  
830 830
class AutocompleteDirectory(Directory):
831 831
    def _q_lookup(self, component):
832
        url = get_session().get_data_source_query_url_from_token(component)
833
        if not url:
832
        info = get_session().get_data_source_query_info_from_token(component)
833
        if not info:
834 834
            raise AccessForbiddenError()
835
        get_response().set_content_type('application/json')
836
        if info.startswith('carddef:'):
837
            from wcs.carddef import CardDef
838
            values = CardDef.get_data_source_items(info,
839
                    query=get_request().form['q'],
840
                    limit=get_request().form.get('page_limit'))
841
            return json.dumps({'data': [{'id': x['id'], 'text': x['text']} for x in values]})
842
        url = info
835 843
        url += urllib.quote(get_request().form['q'])
836
        unsigned_url = url
837 844
        url = sign_url_auto_orig(url)
838 845
        get_response().set_content_type('application/json')
839 846
        return misc.urlopen(url).read()
wcs/carddef.py
19 19

  
20 20
from quixote import get_publisher
21 21
from .qommon import _, N_, misc
22
from .qommon.storage import Equal, NotEqual
22
from .qommon.storage import Equal, NotEqual, ILike
23 23

  
24 24
from wcs.carddata import CardData
25 25
from wcs.formdef import FormDef
......
154 154
            yield (data_source_id, '%s - %s' % (carddef['name'], custom_view.title), data_source_id)
155 155

  
156 156
    @classmethod
157
    def get_data_source_items(cls, data_source_id):
157
    def get_data_source_items(cls, data_source_id, query=None, limit=None):
158 158
        assert data_source_id.startswith('carddef:')
159 159
        parts = data_source_id.split(':')
160 160
        try:
......
178 178
            criterias.extend(form_page.get_view_criterias())
179 179
            order_by = custom_view.order_by
180 180

  
181
        if query:
182
            criterias.append(ILike('digest', query))
181 183
        items = [x.get_data_source_structured_item()
182 184
                 for x in carddef.data_class().select(
183 185
                     clause=criterias,
184
                     order_by=order_by)]
186
                     order_by=order_by,
187
                     limit=limit)]
185 188
        if order_by is None:
186 189
            items.sort(key=lambda x: misc.simplify(x['text']))
187 190
        return items
wcs/data_sources.py
335 335
            return True
336 336
        if self.type == 'json' and self.query_parameter:
337 337
            return True
338
        if self.type and self.type.startswith('carddef:'):
339
            return True
338 340
        return False
339 341

  
340 342
    def migrate(self):
......
416 418
            return self.data_source.get('value')
417 419
        if self.type == 'json' and self.query_parameter:
418 420
            return '/api/autocomplete/%s' % (
419
                    get_session().get_data_source_query_url_token(self.get_json_query_url()))
421
                    get_session().get_data_source_query_info_token(self.get_json_query_url()))
422
        if self.type and self.type.startswith('carddef:'):
423
            return '/api/autocomplete/%s' % (
424
                    get_session().get_data_source_query_info_token(self.type))
420 425
        return None
421 426

  
422 427
    def get_value_by_id(self, param_name, param_value):
wcs/sessions.py
142 142
                del session.visiting_objects[object_key]
143 143
                session.store()
144 144

  
145
    def get_data_source_query_url_token(self, url):
145
    def get_data_source_query_info_token(self, info):
146
        # info can be an URL in case of remote JSON source or a cards
147
        # reference, like carddef:foobar. It is stored in a dictionary
148
        # with _url_ in its name for historical reasons.
146 149
        if not self.data_source_query_url_tokens:
147 150
            self.data_source_query_url_tokens = {}
148 151
        for key, value in self.data_source_query_url_tokens.items():
149
            if value == url:
152
            if value == info:
150 153
                return key
151 154
        key = str(uuid.uuid4())
152
        self.data_source_query_url_tokens[key] = url
155
        self.data_source_query_url_tokens[key] = info
153 156
        self.store()
154 157
        return key
155 158

  
156
    def get_data_source_query_url_from_token(self, token):
159
    def get_data_source_query_info_from_token(self, token):
157 160
        if not self.data_source_query_url_tokens:
158 161
            return None
159 162
        return self.data_source_query_url_tokens.get(token)
160
-