From 9a63bb11e0cb6176aa320c8076dbe4c99ba325e5 Mon Sep 17 00:00:00 2001 From: Ryan Freebern Date: Thu, 30 Jun 2011 10:40:54 -0400 Subject: [PATCH 1/2] Basic RSS feed based on search --- apps/search/feeds.py | 69 ++++++++++++++++++++++++++++ apps/search/helpers.py | 7 ++- apps/search/templates/aggregate.html | 1 + apps/search/templates/results.html | 1 + apps/search/tests/search_tests.py | 8 ++-- apps/search/urls.py | 14 ++++-- 6 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 apps/search/feeds.py diff --git a/apps/search/feeds.py b/apps/search/feeds.py new file mode 100644 index 00000000..f06f3276 --- /dev/null +++ b/apps/search/feeds.py @@ -0,0 +1,69 @@ +from django.contrib.syndication.views import Feed +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ + +from search.helpers import query + + +class PackageFeed(Feed): + search_type = False + search_query = False + + def description(self): + description = 'New ' + if (self.search_type): + description += '%(type)s ' + description += 'packages in the Add-on Builder' + if (self.search_query): + description += ' matching search "%(query)s"' + return _(description) % {'type': self.search_type, + 'query': self.search_query} + + def get_object(self, request, type_): + self.search_type = type_ + self.search_query = request.GET.get('q', '') + if type_ == 'combined': + self.search_type = None + return query(self.search_query, self.search_type, user=request.user, + filter_by_user=False, page=1, limit=20, + score_on='created_at') + + def title(self): + title = 'Add-on Builder: New ' + if self.search_type: + title += '%(type)s ' + title += 'packages' + if (self.search_query): + title += ' matching search "%(query)s"' + return _(title) % {'type': self.search_type, + 'query': self.search_query} + + def link(self): + if self.search_type in ['addon', 'library']: + url = reverse('search_by_type', args=[self.search_type]) + else: + url = reverse('search.combined') + if self.search_query: + url += '?q=%s' % self.search_query + return url + + def items(self, data): + return data['pager'].object_list + + def item_title(self, item): + return item.full_name + + def item_description(self, item): + return item.description + + def item_link(self, item): + return item.get_absolute_url() + + def item_author_name(self, item): + return item.author.get_full_name() + + def item_author_link(self, item): + return item.get_author_profile_url() + + def item_pubdate(self, item): + return item.created_at diff --git a/apps/search/helpers.py b/apps/search/helpers.py index 659f6c81..d4c692a8 100644 --- a/apps/search/helpers.py +++ b/apps/search/helpers.py @@ -5,7 +5,7 @@ from jetpack.models import Package def query(searchq, type_=None, user=None, filter_by_user=False, page=1, - limit=20): + limit=20, score_on=None): get_packages = lambda x: Package.objects.manual_order( [z['_id'] for z in x['hits']['hits']]).active() @@ -14,6 +14,7 @@ def query(searchq, type_=None, user=None, filter_by_user=False, page=1, # to deal with version_text='initial' or 'copy' # nested awesomenezz! query = dict(text=dict(_all=searchq)) if searchq else dict(match_all={}) + fq = dict( filtered=dict( query=query, @@ -45,6 +46,8 @@ def query(searchq, type_=None, user=None, filter_by_user=False, page=1, if filters: q = q.filter(**filters) + if score_on: + q.score(script='_score * doc[\'%s\'].value' % score_on) try: page = int(page) @@ -59,7 +62,7 @@ def query(searchq, type_=None, user=None, filter_by_user=False, page=1, ) if user and user.is_authenticated(): - facet = q.get_facet('author') + facet = q.get_facet('author') data['my_total'] = facet.values()[0] if facet else 0 return data diff --git a/apps/search/templates/aggregate.html b/apps/search/templates/aggregate.html index 7b8f2ae8..7f178365 100644 --- a/apps/search/templates/aggregate.html +++ b/apps/search/templates/aggregate.html @@ -3,6 +3,7 @@ {% block title %}{% if q %}{{q}} - {% endif %}Search - {% endblock %} {% block head %} + {% endblock %} diff --git a/apps/search/templates/results.html b/apps/search/templates/results.html index 93c594dd..12dc099b 100644 --- a/apps/search/templates/results.html +++ b/apps/search/templates/results.html @@ -3,6 +3,7 @@ {% block title %}{% if q %}{{q}} - {% endif %}Search - {% endblock %} {% block head %} + {% endblock %} diff --git a/apps/search/tests/search_tests.py b/apps/search/tests/search_tests.py index a937b91c..f45e0a38 100644 --- a/apps/search/tests/search_tests.py +++ b/apps/search/tests/search_tests.py @@ -1,11 +1,9 @@ import commonware -from django.conf import settings from django.contrib.auth.models import User from nose.tools import eq_ from pyes import StringQuery, FieldQuery, FieldParameter -from elasticutils import S from elasticutils.tests import ESTestCase from jetpack.models import Package @@ -24,8 +22,8 @@ def create_library(name): def create_package(name, type, **kwargs): u = User.objects.get(username='john') - return Package.objects.create(full_name=name, author=u, type=type, **kwargs) - + return Package.objects.create(full_name=name, author=u, type=type, + **kwargs) class TestSearch(ESTestCase): @@ -119,7 +117,6 @@ class QueryTest(ESTestCase): fixtures = ('mozilla_user', 'users', 'core_sdk') - def test_initial_packages_excluded(self): bar = create_addon('super bar') create_addon('super baz') @@ -147,6 +144,7 @@ class QueryTest(ESTestCase): eq_(1, data['total']) + class AggregateQueryTest(ESTestCase): """ search.helpers.aggregate is used to be able to show results from both diff --git a/apps/search/urls.py b/apps/search/urls.py index 48c804d1..830ac2a5 100644 --- a/apps/search/urls.py +++ b/apps/search/urls.py @@ -1,14 +1,18 @@ from django.conf.urls.defaults import url, patterns +from search.feeds import PackageFeed urlpatterns = patterns('search.views', url(r'^$', 'search_home', name='search'), - + url(r'^combined/$', 'combined', name='search.combined'), - + url(r'^(?Paddon|library)/$', 'search_by_type', name='search_by_type'), - + + url(r'^(?Paddon|library|combined)/rss/$', PackageFeed()), + url(r'^me/$', 'me', name='search.me'), - - url(r'^me/(?Paddon|library)/$', 'me_by_type', name='search.me.by_type'), + + url(r'^me/(?Paddon|library)/$', 'me_by_type', + name='search.me.by_type'), ) From 89025012dde5a05d1e8ccd53ba3eefff83c66d75 Mon Sep 17 00:00:00 2001 From: Ryan Freebern Date: Thu, 14 Jul 2011 09:31:10 -0400 Subject: [PATCH 2/2] Custom score query test --- apps/search/tests/search_tests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/search/tests/search_tests.py b/apps/search/tests/search_tests.py index f45e0a38..c839c846 100644 --- a/apps/search/tests/search_tests.py +++ b/apps/search/tests/search_tests.py @@ -143,6 +143,22 @@ class QueryTest(ESTestCase): data = query('foo') eq_(1, data['total']) + def test_custom_scoring(self): + baz = create_addon('score baz') + baz.latest.set_version('1.0') + + quux = create_addon('score quux') + quux.latest.set_version('1.0') + quux.latest.set_version('1.1') + + self.es.refresh() + + data = query('score', score_on='version') + """ + Since quux has more versions than baz, it will have a higher score + and should be the first result. + """ + eq_([p.name for p in data['pager'].object_list], [quux.name, baz.name]) class AggregateQueryTest(ESTestCase):