Projet

Général

Profil

0002-add-internal-page-contents-search-service-6793.patch

Frédéric Péters, 05 mars 2017 09:13

Télécharger (8,3 ko)

Voir les différences:

Subject: [PATCH 2/2] add internal page contents search service (#6793)

 combo/apps/search/models.py                        | 12 ++++---
 .../templates/combo/search-cell-results.html       |  2 +-
 combo/data/models.py                               |  6 ++--
 combo/public/urls.py                               |  1 +
 combo/public/views.py                              | 27 ++++++++++++++
 tests/settings.py                                  |  3 ++
 tests/test_search.py                               | 42 ++++++++++++++++++----
 7 files changed, 78 insertions(+), 15 deletions(-)
combo/apps/search/models.py
20 20
from django import template
21 21
from django.http import HttpResponse
22 22
from django.core.exceptions import PermissionDenied
23
from django.core.urlresolvers import reverse
23 24
from django.forms import models as model_forms, Select
24 25

  
25 26
from combo.utils import requests
......
36 37
    class Meta:
37 38
        verbose_name = _('Search')
38 39

  
39
    @classmethod
40
    def is_enabled(cls):
41
        return bool(getattr(settings, 'COMBO_SEARCH_SERVICES', {}))
42

  
43 40
    def is_visible(self, user=None):
41
        if not self.search_service:
42
            return False
44 43
        return super(SearchCell, self).is_visible(user=user)
45 44

  
46 45
    def get_default_form_class(self):
47 46
        search_services = [(None, _('Not configured'))]
47
        search_services.append(('_text', _('Page Contents')))
48 48
        search_services.extend([(code, service['label'])
49 49
                for code, service in getattr(settings, 'COMBO_SEARCH_SERVICES', {}).items()])
50 50
        widgets = {'_search_service': Select(choices=search_services)}
......
61 61

  
62 62
    @property
63 63
    def search_service(self):
64
        if self._search_service == '_text':
65
            return {'url': reverse('api-search') + '?q=%(q)s', 'label': _('Page Contents')}
64 66
        return settings.COMBO_SEARCH_SERVICES.get(self._search_service) or {}
65 67

  
66 68
    def modify_global_context(self, context, request):
......
99 101
        query = request.GET.get('q')
100 102
        if query and cell.search_service.get('url'):
101 103
            url = cell.search_service.get('url') % {'q': query}
104
            if url.startswith('/'):
105
                url = request.build_absolute_uri(url)
102 106
            results = requests.get(url, cache_duration=0).json()
103 107
        else:
104 108
            results = {'err': 0, 'data': []}
combo/apps/search/templates/combo/search-cell-results.html
2 2
<ul>
3 3
  {% for item in results.data %}
4 4
  <li><a href="{{ item.url }}">{{ item.text }}</a>{% if item.description %}
5
    {{ item.description|safe }}
5
          {% if item.description %}<div>{{ item.description|safe }}</div>{% endif %}
6 6
  {% endif %}</li>
7 7
  {% endfor %}
8 8
</ul>
combo/data/models.py
471 471
    def render_for_search(self):
472 472
        context = Context({'synchronous': True})
473 473
        if not self.is_enabled():
474
            return
474
            return ''
475 475
        if not self.is_visible(user=None):
476
            return
476
            return ''
477 477
        if not self.is_relevant(context):
478
            return
478
            return ''
479 479
        from HTMLParser import HTMLParser
480 480
        return HTMLParser().unescape(strip_tags(self.render(context)))
481 481

  
combo/public/urls.py
20 20

  
21 21
urlpatterns = patterns('combo.publicviews',
22 22
    url(r'^api/menu-badges/', views.menu_badges),
23
    url(r'^api/search/', views.api_search, name='api-search'),
23 24
    url(r'^ajax/cell/(?P<page_pk>\w+)/(?P<cell_reference>[\w_-]+)/$',
24 25
        views.ajax_page_cell, name='combo-public-ajax-page-cell'),
25 26
    (r'__style__/$', views.style),
combo/public/views.py
38 38
from django.utils.translation import ugettext as _
39 39
from django.forms.widgets import Media
40 40

  
41
from haystack.query import SearchQuerySet
42

  
41 43
if 'mellon' in settings.INSTALLED_APPS:
42 44
    from mellon.utils import get_idps
43 45
else:
44 46
    get_idps = lambda: []
45 47

  
46 48
from combo.data.models import CellBase, Page, ParentContentCell, TextCell
49
from combo.apps.search.models import SearchCell
47 50
from combo import utils
48 51

  
49 52

  
......
342 345
        if badge:
343 346
            badges[cell.page_id] = badge
344 347
    return HttpResponse(json.dumps(badges))
348

  
349

  
350
def api_search(request):
351
    for cell in SearchCell.objects.filter(_search_service='_text'):
352
        if not cell.is_visible(request.user):
353
            continue
354
        break
355
    else:
356
        raise Http404()
357
    query = request.GET.get('q') or ''
358
    searchqueryset = SearchQuerySet()
359
    sqs = searchqueryset.auto_query(query).highlight()
360
    sqs.load_all()
361
    hits = []
362
    for page in sqs:
363
        if page.highlighted['text']:
364
            description = '<p>%s</p>' % page.highlighted['text'][0]
365
        hits.append({
366
            'text': page.title,
367
            'url': page.url,
368
            'description': description,
369
        })
370

  
371
    return HttpResponse(json.dumps({'data': hits}), content_type='application/json')
tests/settings.py
24 24
import tempfile
25 25
MEDIA_ROOT = tempfile.mkdtemp('combo-test')
26 26

  
27
HAYSTACK_CONNECTIONS['default']['PATH'] = os.path.join(
28
        tempfile.mkdtemp('combo-test-whoosh'))
29

  
27 30
if 'DISABLE_MIGRATIONS' in os.environ:
28 31
    class DisableMigrations(object):
29 32
        def __contains__(self, item):
tests/test_search.py
36 36
    def __exit__(self, *args, **kwargs):
37 37
        delattr(settings, 'COMBO_SEARCH_SERVICES')
38 38

  
39
def test_enabled(app):
40
    assert SearchCell.is_enabled() == False
41
    with SearchServices(SEARCH_SERVICES):
42
        assert SearchCell.is_enabled() == True
43
    with SearchServices({}):
44
        assert SearchCell.is_enabled() == False
45

  
46 39
def test_search_cell(app):
47 40
    with SearchServices(SEARCH_SERVICES):
48 41
        page = Page(title='Search', slug='search_page', template_name='standard')
......
165 158
    prepared_data = page_index.prepare(page)
166 159
    assert page.title in prepared_data['text']
167 160
    assert 'foobar' in prepared_data['text']
161

  
162
def test_search_api(app):
163
    page = Page(title='example page', slug='example-page')
164
    page.save()
165

  
166
    cell = TextCell(page=page, text='<p>foobar baz</p>', order=0)
167
    cell.save()
168

  
169
    second_page = Page(title='second page', slug='second-page')
170
    second_page.save()
171

  
172
    cell = TextCell(page=second_page, text='<p>other baz</p>', order=0)
173
    cell.save()
174

  
175
    page_index = PageIndex()
176
    page_index.reindex()
177

  
178
    resp = app.get('/api/search/?q=foobar', status=404)
179

  
180
    cell = SearchCell(page=page, _search_service='_text', order=0)
181
    cell.save()
182

  
183
    resp = app.get('/api/search/?q=foobar', status=200)
184
    assert len(resp.json['data']) == 1
185
    assert resp.json['data'][0]['text'] == 'example page'
186

  
187
    resp = app.get('/api/search/?q=other', status=200)
188
    assert len(resp.json['data']) == 1
189
    assert resp.json['data'][0]['text'] == 'second page'
190

  
191
    resp = app.get('/api/search/?q=baz', status=200)
192
    assert len(resp.json['data']) == 2
193

  
194
    resp = app.get('/api/search/?q=quux', status=200)
195
    assert len(resp.json['data']) == 0
168
-