Merge lp:~elachuni/ubuntu-webcatalog/distro-searches into lp:ubuntu-webcatalog

Proposed by Anthony Lenton
Status: Merged
Approved by: Anthony Lenton
Approved revision: 40
Merged at revision: 41
Proposed branch: lp:~elachuni/ubuntu-webcatalog/distro-searches
Merge into: lp:ubuntu-webcatalog
Diff against target: 285 lines (+109/-31)
5 files modified
src/webcatalog/models/applications.py (+1/-1)
src/webcatalog/templates/webcatalog/search_results.html (+19/-0)
src/webcatalog/tests/test_views.py (+67/-24)
src/webcatalog/urls.py (+1/-0)
src/webcatalog/views.py (+21/-6)
To merge this branch: bzr merge lp:~elachuni/ubuntu-webcatalog/distro-searches
Reviewer Review Type Date Requested Status
Matthew Nuzum (community) Approve
Review via email: mp+67257@code.launchpad.net

Commit message

Make searches distro-series specific

Description of the change

Overview
========
Make searches distro-series specific

Details
=======
Until now, search results would include apps for all distroseries, repeated and intermixed. So if an app is available for four different distroseries, you would get it repeated four times in the search results.
While coding the solution different options were considered:
 - Showing an app in search results only once, if it's available in any distroseries. This would require a largeish model change to separate an App from an AppInADistroSeries, with many AppInADistroSeries for each App. And you still never know what distroseries an app you see in your search results is available for.
 - Leave search results as is, but add a small label next to each app stating which distroseries it's for. This would still result in multiple instances of almost identical apps in your search results.
 - Group search results by distroseries. This is what packages.ubuntu.com does, but it doesn't play well with paging results.
 - The current solution, that's to have the search limit to a default distroseries, and allow the user to specify a different distroseries easily (using a right navigation bar in this case).

While I was there, I made distroseries show up in a reverse code_name order in our right sidebars, so that Oneiric is always at the top, and older distroseries are shown further down.

To post a comment you must log in.
Revision history for this message
Matthew Nuzum (newz) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/models/applications.py'
2--- src/webcatalog/models/applications.py 2011-07-07 14:15:04 +0000
3+++ src/webcatalog/models/applications.py 2011-07-07 21:04:30 +0000
4@@ -104,7 +104,7 @@
5 def available_distroseries(self):
6 """Return the set of distroseries for which this app is available"""
7 return DistroSeries.objects.filter(
8- application__package_name=self.package_name).order_by('version')
9+ application__package_name=self.package_name).order_by('-code_name')
10
11 def update_departments(self):
12 """Update the list of departments for this app"""
13
14=== modified file 'src/webcatalog/templates/webcatalog/search_results.html'
15--- src/webcatalog/templates/webcatalog/search_results.html 2011-06-28 15:03:12 +0000
16+++ src/webcatalog/templates/webcatalog/search_results.html 2011-07-07 21:04:30 +0000
17@@ -7,6 +7,23 @@
18 {% block content %}
19 {% include "webcatalog/breadcrumbs_snippet.html" %}
20
21+ <div id="rightbar">
22+ <div class="portlet">
23+ <div class="portletheader">Search within</div>
24+ <ul class="portletactions">
25+ {% for ds in available_distroseries %}
26+ <li class="item{% ifequal ds.code_name distroseries %} active{% endifequal %}{% if forloop.last %} last{% endif %}">
27+ {% ifequal ds.code_name distroseries %}
28+ <span>Ubuntu {{ ds.version }} ({{ ds.code_name }})</span>
29+ {% else %}
30+ <a href="{% url wc-search ds.code_name %}?q={{query}}">Ubuntu {{ ds.version }} ({{ ds.code_name }})</a>
31+ {% endifequal %}</li>
32+ {% endfor %}
33+ </ul>
34+ </div>
35+ </div>
36+
37+<div class="main-column">
38 {% if query %}
39 <h3>{% blocktrans %}Searched for "{{query}}"{% endblocktrans %}</h3>
40 {% if page.object_list %}
41@@ -21,4 +38,6 @@
42 {% else %}
43 <h3>{% trans "You didn't search for anything!" %}</h3>
44 {% endif %}
45+</div>
46+
47 {% endblock %}
48
49=== modified file 'src/webcatalog/tests/test_views.py'
50--- src/webcatalog/tests/test_views.py 2011-07-04 14:47:05 +0000
51+++ src/webcatalog/tests/test_views.py 2011-07-07 21:04:30 +0000
52@@ -212,8 +212,9 @@
53 class SearchTestCase(TestCaseWithFactory):
54 def test_no_search_retrieves_no_apps(self):
55 # Ensure there's some data in the DB
56- self.factory.make_application()
57- url = reverse('wc-search')
58+ distro = self.factory.make_distroseries(code_name='lucid')
59+ self.factory.make_application(distroseries=distro)
60+ url = reverse('wc-search', args=[distro.code_name])
61 for data in [{'q': ''}, {}]:
62
63 response = self.client.get(url, data=data)
64@@ -222,10 +223,14 @@
65 self.assertContains(response, "You didn't search for anything")
66
67 def test_search_searches_package_name(self):
68- app1 = self.factory.make_application(package_name='foo')
69- app2 = self.factory.make_application(package_name='bar')
70- app3 = self.factory.make_application(package_name='foobar')
71- url = reverse('wc-search')
72+ distro = self.factory.make_distroseries(code_name='lucid')
73+ app1 = self.factory.make_application(package_name='foo',
74+ distroseries=distro)
75+ app2 = self.factory.make_application(package_name='bar',
76+ distroseries=distro)
77+ app3 = self.factory.make_application(package_name='foobar',
78+ distroseries=distro)
79+ url = reverse('wc-search', args=[distro.code_name])
80 expected = {'foo': set([app1, app3]), 'bar': set([app2, app3])}
81 for q in expected:
82
83@@ -235,10 +240,14 @@
84 expected[q], set(response.context['page'].object_list))
85
86 def test_search_searches_name(self):
87- app1 = self.factory.make_application(name='foo')
88- app2 = self.factory.make_application(name='bar')
89- app3 = self.factory.make_application(name='foobar')
90- url = reverse('wc-search')
91+ distro = self.factory.make_distroseries(code_name='lucid')
92+ app1 = self.factory.make_application(name='foo',
93+ distroseries=distro)
94+ app2 = self.factory.make_application(name='bar',
95+ distroseries=distro)
96+ app3 = self.factory.make_application(name='foobar',
97+ distroseries=distro)
98+ url = reverse('wc-search', args=[distro.code_name])
99 expected = {'foo': set([app1, app3]), 'bar': set([app2, app3])}
100 for q in expected:
101
102@@ -248,43 +257,53 @@
103 expected[q], set(response.context['page'].object_list))
104
105 def test_search_no_apps_found_says_so(self):
106- url = reverse('wc-search')
107+ distro = self.factory.make_distroseries(code_name='lucid')
108+ url = reverse('wc-search', args=[distro.code_name])
109
110 response = self.client.get(url, data={'q': 'sarasa'})
111
112 self.assertContains(response, "No applications found")
113
114 def test_response_shows_query_if_no_apps_found(self):
115+ distro = self.factory.make_distroseries(code_name='lucid')
116 query = 'someimprobablequery'
117- url = reverse('wc-search')
118+ url = reverse('wc-search', args=[distro.code_name])
119
120 response = self.client.get(url, data={'q': query})
121
122 self.assertContains(response, query)
123
124 def test_response_shows_query_if_some_apps_found(self):
125+ distro = self.factory.make_distroseries(code_name='lucid')
126 query = 'someimprobablequery'
127- self.factory.make_application(package_name=query)
128- url = reverse('wc-search')
129+ self.factory.make_application(package_name=query, distroseries=distro)
130+ url = reverse('wc-search', args=[distro.code_name])
131
132 response = self.client.get(url, data={'q': query})
133
134 self.assertContains(response, query)
135
136 def test_response_shows_number_of_apps_found(self):
137- app1 = self.factory.make_application(package_name='foo')
138- app2 = self.factory.make_application(package_name='foobar')
139- url = reverse('wc-search')
140+ distro = self.factory.make_distroseries(code_name='lucid')
141+ app1 = self.factory.make_application(package_name='foo',
142+ distroseries=distro)
143+ app2 = self.factory.make_application(package_name='foobar',
144+ distroseries=distro)
145+ url = reverse('wc-search', args=[distro.code_name])
146
147 response = self.client.get(url, data={'q': 'foo'})
148
149 self.assertContains(response, "1 to 2 of 2")
150
151 def test_response_shows_paginated_results(self):
152- app1 = self.factory.make_application(name='foo')
153- app2 = self.factory.make_application(name='fobar')
154- app3 = self.factory.make_application(name='foobar')
155- url = reverse('wc-search')
156+ distro = self.factory.make_distroseries(code_name='lucid')
157+ app1 = self.factory.make_application(name='foo',
158+ distroseries=distro)
159+ app2 = self.factory.make_application(name='fobar',
160+ distroseries=distro)
161+ app3 = self.factory.make_application(name='foobar',
162+ distroseries=distro)
163+ url = reverse('wc-search', args=[distro.code_name])
164
165 with patch_settings(PAGE_BATCH_SIZE=2):
166 response = self.client.get(url, data={'q': 'fo'})
167@@ -299,8 +318,9 @@
168 self.assertContains(response, '<a href="?q=fo&page=2"', 2)
169
170 def test_invalid_page_doesnt_error(self):
171- self.factory.make_application(name='foo')
172- url = reverse('wc-search')
173+ distro = self.factory.make_distroseries(code_name='lucid')
174+ self.factory.make_application(name='foo', distroseries=distro)
175+ url = reverse('wc-search', args=[distro.code_name])
176
177 response = self.client.get(url, data={'q': 'fo', 'page': 'aef8'})
178 page = response.context['page']
179@@ -314,6 +334,11 @@
180 page = response.context['page']
181 self.assertEqual(1, page.number)
182
183+ def test_invalid_distroseries_returns_404(self):
184+ response = self.client.get(reverse('wc-search', args=['zirconic']))
185+
186+ self.assertEqual(404, response.status_code)
187+
188
189 class OverviewTestCase(TestCaseWithFactory):
190 def test_index_contains_links_to_departments(self):
191@@ -413,4 +438,22 @@
192 response = self.client.get(reverse('wc-department', args=[
193 'amnesiac', dept.id]))
194
195- self.assertEqual(404, response.status_code)
196\ No newline at end of file
197+ self.assertEqual(404, response.status_code)
198+
199+ def test_distroseries_are_ordered_by_reverse_code_name(self):
200+ dept = self.factory.make_department('bar')
201+ default = self.factory.make_distroseries(
202+ code_name=settings.DEFAULT_DISTRO, version='11.04')
203+ lucid = self.factory.make_distroseries(code_name='lucid',
204+ version='10.04')
205+ maverick = self.factory.make_distroseries(code_name='maverick',
206+ version='10.10')
207+
208+ response = self.client.get(reverse('wc-department', args=[
209+ settings.DEFAULT_DISTRO, dept.id]))
210+
211+ url = reverse('wc-department', args=['lucid', dept.id])
212+ lucid_pos = response.content.find('<a href="{0}">Ubuntu'.format(url))
213+ url = reverse('wc-department', args=['maverick', dept.id])
214+ maver_pos = response.content.find('<a href="{0}">Ubuntu'.format(url))
215+ self.assertTrue(lucid_pos > maver_pos)
216
217=== modified file 'src/webcatalog/urls.py'
218--- src/webcatalog/urls.py 2011-07-01 16:18:58 +0000
219+++ src/webcatalog/urls.py 2011-07-07 21:04:30 +0000
220@@ -40,6 +40,7 @@
221 url(r'^applications/(?P<package_name>[-.+\w]+)/$', 'application_detail',
222 name="wc-package-detail"),
223 url(r'^search/$', 'search', name="wc-search"),
224+ url(r'^search/(?P<distro>[-.+\w]+)/$', 'search', name="wc-search"),
225
226 (r'^api/', include('webcatalog.api.urls')),
227
228
229=== modified file 'src/webcatalog/views.py'
230--- src/webcatalog/views.py 2011-07-04 15:46:48 +0000
231+++ src/webcatalog/views.py 2011-07-07 21:04:30 +0000
232@@ -59,17 +59,30 @@
233 return 1
234
235
236-def search(request):
237+def search(request, distro=None):
238+ query = request.GET.get('q', '')
239+ if distro is None:
240+ useragent = UserAgentString(request.META.get('HTTP_USER_AGENT', ''))
241+ # Check for the distroseries in the useragent, if we have it,
242+ # redirect there.
243+ if useragent.distroseries:
244+ distro = useragent.distroseries
245+ else:
246+ distro = settings.DEFAULT_DISTRO
247+ return HttpResponseRedirect(reverse('wc-search', args=[distro]) +
248+ '?' + urlencode({'q': query}))
249+
250+ # Attempt to retrieve the DistroSeries to ensure that it actually exists
251+ get_object_or_404(DistroSeries, code_name=distro)
252+
253 apps = []
254- query = ''
255 if 'q' in request.GET:
256- query = request.GET.get('q', '')
257 terms = query.split()
258 if terms:
259 ors = [Q(package_name__icontains=term) for term in terms]
260 ors += [Q(name__icontains=term) for term in terms]
261- apps = Application.objects.filter(reduce(operator.or_, ors))
262- apps = apps.order_by('name')
263+ apps = Application.objects.filter(distroseries__code_name=distro)
264+ apps = apps.filter(reduce(operator.or_, ors)).order_by('name')
265
266 crumbs = [{'name': 'Get Software', 'url': reverse('wc-index')},
267 {'name': 'Search results', 'url': reverse('wc-search') + '?' +
268@@ -83,6 +96,8 @@
269 'query': query,
270 'page': paginator.page(page_num),
271 'breadcrumbs': crumbs,
272+ 'available_distroseries': DistroSeries.objects.order_by('-code_name'),
273+ 'distroseries': distro,
274 })
275 return render_to_response('webcatalog/search_results.html',
276 context_instance=context)
277@@ -125,7 +140,7 @@
278 'subdepts': subdepts,
279 'page': paginator.page(page_num),
280 'breadcrumbs': dept.crumbs(),
281- 'available_distroseries': DistroSeries.objects.all(),
282+ 'available_distroseries': DistroSeries.objects.order_by('-code_name'),
283 'distroseries': distro,
284 })
285 return render_to_response('webcatalog/department_overview.html',

Subscribers

People subscribed via source and target branches