Projet

Général

Profil

0002-search-search-on-pages-and-sub-pages-40224.patch

Lauréline Guérin, 26 mars 2020 14:37

Télécharger (7,77 ko)

Voir les différences:

Subject: [PATCH 2/2] search: search on pages and sub pages (#40224)

 combo/apps/search/forms.py  | 20 +++++++++++++++++--
 combo/apps/search/models.py | 27 +++++++++++++++++++------
 combo/apps/search/utils.py  |  7 ++++++-
 tests/test_search.py        | 40 +++++++++++++++++++++++++++++++++++++
 4 files changed, 85 insertions(+), 9 deletions(-)
combo/apps/search/forms.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
from django import forms
18
from django.utils.translation import ugettext_lazy as _
18 19

  
20
from combo.data.models import Page
19 21
from combo.utils.forms import MultiSortWidget
20 22

  
21
from . import engines
23
from . import engines as search_engines
22 24
from .models import SearchCell
23 25

  
24 26

  
......
27 29
        model = SearchCell
28 30
        fields = ('_search_services', 'autofocus')
29 31

  
32
    def get_engines(self):
33
        engines = search_engines.get_engines()
34
        if '_text' not in engines:
35
            return engines
36

  
37
        text_engine = engines['_text']
38
        for i, page in enumerate(Page.objects.filter(snapshot__isnull=True, sub_slug='', root_for_search=True)):
39
            engines['_text_page_{}'.format(page.slug)] = {
40
                'function': text_engine['function'],
41
                'label': _('Page "%(page)s" and sub pages Contents') % {'page': page.title},
42
            }
43
        return engines
44

  
30 45
    def __init__(self, *args, **kwargs):
31 46
        super(SearchCellForm, self).__init__(*args, **kwargs)
32
        options = [(x, engines.get_engines()[x]['label']) for x in engines.get_engines().keys()]
47
        engines = self.get_engines()
48
        options = [(x, engines[x]['label']) for x in engines.keys()]
33 49
        self.fields['_search_services'].widget = MultiSortWidget(choices=options,
34 50
                                                                 with_checkboxes=True)
combo/apps/search/models.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import os
18

  
19 17
from django.contrib.auth.models import Group
20 18
from django.contrib.contenttypes import fields
21 19
from django.contrib.contenttypes.models import ContentType
......
37 35
from . import engines
38 36

  
39 37

  
38
def get_root_page_and_children(service_slug):
39
    if not service_slug.startswith('_text_page_'):
40
        return
41

  
42
    page_slug = service_slug.replace('_text_page_', '')
43
    try:
44
        root_page = Page.objects.get(slug=page_slug, root_for_search=True)
45
    except (Page.DoesNotExist, Page.MultipleObjectsReturned):
46
        return
47

  
48
    return root_page.get_descendants_and_me()
49

  
50

  
40 51
@register_cell_class
41 52
class SearchCell(CellBase):
42 53
    template_name = 'combo/search-cell.html'
......
69 80
        services = []
70 81
        for service_slug in self._search_services.get('data') or []:
71 82
            service = engines.get(service_slug)
83
            if service_slug.startswith('_text_page_'):
84
                service = engines.get('_text')
72 85
            if service and (service.get('url') or service.get('function')):
73 86
                service['slug'] = service_slug
74 87
                services.append(service)
......
142 155
            return render_response(service)
143 156

  
144 157
        if service.get('function'):  # internal search engine
145
            results = {'data': service['function'](request, query)}
158
            pages = get_root_page_and_children(service_slug)
159
            results = {'data': service['function'](request, query, pages=pages)}
146 160
        else:
147
            url = get_templated_url(service['url'],
148
                    context={'request': request, 'q': query, 'search_service': service})
161
            url = get_templated_url(
162
                service['url'],
163
                context={'request': request, 'q': query, 'search_service': service})
149 164
            url = url % {'q': quote(query.encode('utf-8'))}  # if url contains %(q)s
150 165
            if url.startswith('/'):
151 166
                url = request.build_absolute_uri(url)
......
186 201
        return render_response(service, results)
187 202

  
188 203
    def has_text_search_service(self):
189
        return '_text' in self._search_services.get('data', [])
204
        return any(key.startswith('_text') for key in self._search_services.get('data', []))
190 205

  
191 206
    def missing_index(self):
192 207
        return IndexedCell.objects.all().count() == 0
combo/apps/search/utils.py
77 77
                indexed_cell.save()
78 78

  
79 79

  
80
def search_site(request, query):
80
def search_site(request, query, pages=None):
81
    pages = pages or []
82

  
81 83
    if connection.vendor == 'postgresql':
82 84
        config = settings.POSTGRESQL_FTS_SEARCH_CONFIG
83 85
        vector = SearchVector('title', config=config, weight='A') + SearchVector('indexed_text', config=config, weight='A')
......
94 96
                Q(restricted_groups__in=request.user.groups.all()))
95 97
        qs = qs.exclude(excluded_groups__in=request.user.groups.all())
96 98

  
99
    if pages:
100
        qs = qs.filter(page__in=pages)
101

  
97 102
    hits = []
98 103
    seen = {}
99 104
    for hit in qs:
tests/test_search.py
309 309
    assert resp.text.count('<li') == 0
310 310

  
311 311

  
312
def test_search_on_root_page_api(app):
313
    page = Page.objects.create(title='example page', slug='example-page')
314
    TextCell.objects.create(page=page, text='<p>foobar baz</p>', order=0)
315

  
316
    second_page = Page.objects.create(title='second page', slug='second-page', root_for_search=True)
317
    TextCell.objects.create(page=second_page, text='<p>other baz</p>', order=0)
318

  
319
    index_site()
320

  
321
    cell = SearchCell.objects.create(page=page, _search_services={'data': ['_text']}, order=0)
322
    resp = app.get('/ajax/search/%s/_text/?q=baz' % cell.pk, status=200)
323
    assert resp.text.count('<li') == 2
324

  
325
    cell._search_services = {'data': ['_text_page_second-page']}
326
    cell.save()
327
    resp = app.get('/ajax/search/%s/_text_page_second-page/?q=baz' % cell.pk, status=200)
328
    assert resp.text.count('<li') == 1
329

  
330
    # invalid page (not root for search), search everywhere
331
    cell._search_services = {'data': ['_text_page_example-page']}
332
    cell.save()
333
    resp = app.get('/ajax/search/%s/_text_page_example-page/?q=baz' % cell.pk, status=200)
334
    assert resp.text.count('<li') == 2
335

  
336
    # page does not exists, search everywhere
337
    cell._search_services = {'data': ['_text_page_foo']}
338
    cell.save()
339
    resp = app.get('/ajax/search/%s/_text_page_foo/?q=baz' % cell.pk, status=200)
340
    assert resp.text.count('<li') == 2
341

  
342
    # slug is not unique, search everywhere
343
    page.slug = 'second-page'
344
    page.root_for_search = True
345
    page.save()
346
    cell._search_services = {'data': ['_text_page_second-page']}
347
    cell.save()
348
    resp = app.get('/ajax/search/%s/_text_page_second-page/?q=baz' % cell.pk, status=200)
349
    assert resp.text.count('<li') == 2
350

  
351

  
312 352
def test_search_external_links(app):
313 353
    page = Page(title='example page', slug='example-page')
314 354
    page.save()
315
-