Merge lp:~michael.nelson/ubuntu-webcatalog/788210-navigate-distroseries into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Foord
Approved revision: 34
Merged at revision: 28
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/788210-navigate-distroseries
Merge into: lp:ubuntu-webcatalog
Diff against target: 385 lines (+182/-74)
8 files modified
django_project/config/main.cfg (+1/-0)
src/webcatalog/context_processors.py (+5/-0)
src/webcatalog/templates/webcatalog/application_detail.html (+2/-10)
src/webcatalog/templates/webcatalog/install_options_snippet.html (+11/-0)
src/webcatalog/templatetags/webcatalog.py (+81/-0)
src/webcatalog/tests/test_views.py (+48/-4)
src/webcatalog/urls.py (+4/-6)
src/webcatalog/views.py (+30/-54)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/788210-navigate-distroseries
Reviewer Review Type Date Requested Status
Michael Foord (community) Approve
Review via email: mp+66268@code.launchpad.net

Commit message

Redirect to series-specific app if series not provided (and refactor view code into template tag).

Description of the change

Overview
========
This branch does part of bug 788210 - redirecting to an app for a specific distroseries when we know the users distroseries, but is mostly moving existing code around to get ready for the next branch.

Details
=======
 * Moved the fat from the application_detail view into a template tag
 * Refactored the urls, removing the need for a separate application_detail_no_series view (and reordered the args for the view so the resource is consistent)
 * Added a context processor to include the user-agent in the request context (so it can be used in a template tag).

To test: follow the readme to bootstrap and then `fab test`

This branch is landable as is, but in the following branch I plan to:
 * Move the current view tests for the install options into template tag tests (and add some tests - it's telling me an app is not available for my distroseries while displaying the app for my distroseries)
 * Refactor the get_user_os method to hopefully make it a bit more readable (removing distro_is_a_guess if possible)
 * Add the list of other distroseries to the templates (second part of bug 788210)
 * Fix an issue where currently we blindly assume that apps exist for our default distroseries... we should instead be finding the app with the highest released distroseries version (if a specific default is not set)
 * Related to the last one, update the urls to use distroseries *versions* for released distroseries and codenames for unreleased series (consistent with ubuntu convention).

To post a comment you must log in.
Revision history for this message
Michael Foord (mfoord) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'django_project/config/main.cfg'
2--- django_project/config/main.cfg 2011-06-28 15:03:12 +0000
3+++ django_project/config/main.cfg 2011-06-29 09:42:34 +0000
4@@ -49,6 +49,7 @@
5 django.core.context_processors.static
6 django.contrib.messages.context_processors.messages
7 webcatalog.context_processors.google_analytics_id
8+ webcatalog.context_processors.user_agent
9
10 template_dirs = django_project/templates/
11 static_root = ./django_project/static/
12
13=== modified file 'src/webcatalog/context_processors.py'
14--- src/webcatalog/context_processors.py 2011-06-18 23:17:11 +0000
15+++ src/webcatalog/context_processors.py 2011-06-29 09:42:34 +0000
16@@ -24,3 +24,8 @@
17 return {
18 'google_analytics_id': getattr(settings, 'GOOGLE_ANALYTICS_ID', None),
19 }
20+
21+
22+def user_agent(request):
23+ """Adds the browser user-agent string to the context if present."""
24+ return dict(user_agent=request.META.get('HTTP_USER_AGENT', ''))
25
26=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
27--- src/webcatalog/templates/webcatalog/application_detail.html 2011-06-28 15:03:12 +0000
28+++ src/webcatalog/templates/webcatalog/application_detail.html 2011-06-29 09:42:34 +0000
29@@ -1,5 +1,6 @@
30 {% extends "webcatalog/base.html" %}
31 {% load i18n %}
32+{% load webcatalog %}
33
34 {% block title %}Application {{ application.name }}{% endblock %}
35 {% block header %}Details for {{ application.name }}{% endblock %}
36@@ -27,16 +28,7 @@
37 <img src="http://screenshots.ubuntu.com/thumbnail-with-version/{{ application.package_name }}/ignored" />
38 </div>
39 <p>{{ application.description }}</p>
40- {% if hide_button %}
41- {{ message_text }}
42- {% else %}
43- <a href="apt://{{ application.package_name }}" class="awesome">{{ button_text }} {{ application.name }}</a>
44- {% endif %}
45- {% if links %}
46- {% for text, link in links %}
47- <br><a href="{{link}}" class="awesome">{{ text }}</a>
48- {% endfor %}
49- {% endif %}
50+ {% install_options application %}
51 </div>
52 <div class="license">
53 <table>
54
55=== added file 'src/webcatalog/templates/webcatalog/install_options_snippet.html'
56--- src/webcatalog/templates/webcatalog/install_options_snippet.html 1970-01-01 00:00:00 +0000
57+++ src/webcatalog/templates/webcatalog/install_options_snippet.html 2011-06-29 09:42:34 +0000
58@@ -0,0 +1,11 @@
59+{% if hide_button %}
60+{{ message_text }}
61+{% else %}
62+<a href="apt://{{ application.package_name }}" class="awesome">{{ button_text }} {{ application.name }}</a>
63+{% endif %}
64+{% if links %}
65+{% for text, link in links %}
66+ <br><a href="{{link}}" class="awesome">{{ text }}</a>
67+{% endfor %}
68+{% endif %}
69+
70
71=== added directory 'src/webcatalog/templatetags'
72=== added file 'src/webcatalog/templatetags/__init__.py'
73=== added file 'src/webcatalog/templatetags/webcatalog.py'
74--- src/webcatalog/templatetags/webcatalog.py 1970-01-01 00:00:00 +0000
75+++ src/webcatalog/templatetags/webcatalog.py 2011-06-29 09:42:34 +0000
76@@ -0,0 +1,81 @@
77+# -*- coding: utf-8 -*-
78+# This file is part of the Ubuntu Web Catalog
79+# Copyright (C) 2011 Canonical Ltd.
80+#
81+# This program is free software: you can redistribute it and/or modify
82+# it under the terms of the GNU Affero General Public License as
83+# published by the Free Software Foundation, either version 3 of the
84+# License, or (at your option) any later version.
85+#
86+# This program is distributed in the hope that it will be useful,
87+# but WITHOUT ANY WARRANTY; without even the implied warranty of
88+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
89+# GNU Affero General Public License for more details.
90+#
91+# You should have received a copy of the GNU Affero General Public License
92+# along with this program. If not, see <http://www.gnu.org/licenses/>.
93+
94+"""Custom template tags for the webcatalog app."""
95+
96+from __future__ import (
97+ absolute_import,
98+ with_statement,
99+ )
100+
101+__metaclass__ = type
102+__all__ = [
103+ 'install_options',
104+ ]
105+
106+from django import template
107+from django.core.urlresolvers import reverse
108+
109+from webcatalog.models import Application
110+from webcatalog.views import get_user_os
111+
112+register = template.Library()
113+
114+
115+@register.inclusion_tag('webcatalog/install_options_snippet.html',
116+ takes_context=True)
117+def install_options(context, application):
118+ app = application
119+ os = get_user_os(context['user_agent'])
120+ button_text = 'Purchase' if app.for_purchase else 'Install'
121+ right_platform = os['is_linux']
122+ right_arch = app.architectures.find(os['arch']) > -1
123+ right_distro = os['distro'].find(app.distroseries.code_name) > -1
124+ perfect_match = (right_platform and right_distro and right_arch)
125+ message_text = 'Not available for your version of Ubuntu'
126+ links = []
127+ if not perfect_match:
128+ if not right_platform:
129+ message_text = "Not available for your platform"
130+ links.append(('Download Ubuntu', 'http://www.ubuntu.com/download'))
131+ elif not right_distro and os['distro_is_a_guess']:
132+ # see if we have this package for *other* distros
133+ apps_found = Application.objects.filter(
134+ package_name=application.package_name).exclude(
135+ distroseries__code_name=os['distro'])
136+ if apps_found:
137+ message_text = ('Not running %s?' % os['distro'])
138+ for app_found in apps_found:
139+ args = [app.distroseries.code_name, app.package_name]
140+ url = reverse('wc-package-detail', args=args)
141+ text = "%s %s for %s" % (button_text, app.name,
142+ app_found.distroseries.code_name)
143+ links.append((text, url))
144+ elif not right_distro:
145+ app_found = Application.objects.filter(
146+ package_name=application.package_name,
147+ distroseries__code_name=os['distro'])
148+ if app_found:
149+ args = [app.distroseries.code_name, app.package_name]
150+ url = reverse('wc-package-detail', args=args)
151+ message_text = ('%s is also available for your version '
152+ 'of Ubuntu' % app.name)
153+ links.append(("%s for os['distro']" % button_text, url))
154+
155+ return dict(application=application, button_text=button_text,
156+ hide_button=not perfect_match, message_text=message_text,
157+ links=links)
158
159=== modified file 'src/webcatalog/tests/test_views.py'
160--- src/webcatalog/tests/test_views.py 2011-06-28 18:17:25 +0000
161+++ src/webcatalog/tests/test_views.py 2011-06-29 09:42:34 +0000
162@@ -33,8 +33,9 @@
163
164 __metaclass__ = type
165 __all__ = [
166+ 'ApplicationDetailNoSeriesTestCase',
167+ 'ApplicationDetailTestCase',
168 'OverviewTestCase',
169- 'PackageDetailTestCase',
170 'SearchTestCase',
171 ]
172
173@@ -46,7 +47,7 @@
174 'Gecko/20100101 Firefox/4.0.1')
175
176
177-class PackageDetailTestCase(TestCaseWithFactory):
178+class ApplicationDetailTestCase(TestCaseWithFactory):
179
180 def get_app_and_response(self, code_name='natty', version='11.04',
181 arch='x86_64', name=None, comment=None,
182@@ -67,7 +68,7 @@
183 if not detail_package:
184 detail_package = app.package_name
185
186- url = reverse('wc-package-detail', args=[detail_distro, detail_package])
187+ url = reverse('wc-package-detail', args=[detail_package, detail_distro])
188
189 if useragent:
190 response = self.client.get(url, HTTP_USER_AGENT=useragent)
191@@ -138,7 +139,7 @@
192 self.assertContains(response, 'Purchase %s for oneric' % app.name)
193 self.assertContains(response, '/cat/applications/oneric/pkgfoo/')
194
195- @patch('webcatalog.views.get_user_os')
196+ @patch('webcatalog.templatetags.webcatalog.get_user_os')
197 def test_button_for_non_matching_distroseries_and_unavailable(self,
198 mock_get_user_os):
199 # patch user agent check so we can have distro is a guess be False
200@@ -190,6 +191,49 @@
201 settings.DEFAULT_DISTRO)
202
203
204+class ApplicationDetailNoSeriesTestCase(TestCaseWithFactory):
205+
206+ def test_defaults_to_specified_series(self):
207+ # If a distroseries is not included in the url,
208+ # and we cannot determine it from the user agent,
209+ # we redirect to the one specified in the configuration.
210+ natty = self.factory.make_distroseries(code_name='natty')
211+ lucid = self.factory.make_distroseries(code_name='lucid')
212+ natty_app = self.factory.make_application(
213+ package_name='pkgfoo', distroseries=natty)
214+ lucid_app = self.factory.make_application(
215+ package_name='pkgfoo', distroseries=lucid)
216+
217+ for default_distro in ('natty', 'lucid'):
218+ with patch_settings(DEFAULT_DISTRO=default_distro):
219+ url = reverse('wc-package-detail', args=['pkgfoo'])
220+ response = self.client.get(url)
221+
222+ self.assertRedirects(
223+ response, reverse(
224+ 'wc-package-detail', args=['pkgfoo', default_distro]))
225+
226+ def test_redirects_to_ua_distroseries(self):
227+ # If a distroseries is not included in the url, but we
228+ # know it from the user agent, and the app exists for that
229+ # distro series also, we redirect there.
230+ natty = self.factory.make_distroseries(code_name='natty')
231+ lucid = self.factory.make_distroseries(code_name='lucid')
232+ natty_app = self.factory.make_application(
233+ package_name='pkgfoo', distroseries=natty)
234+ lucid_app = self.factory.make_application(
235+ package_name='pkgfoo', distroseries=lucid)
236+
237+ url = reverse('wc-package-detail', args=['pkgfoo'])
238+ with patch_settings(DEFAULT_DISTRO='natty'):
239+ response = self.client.get(
240+ url, HTTP_USER_AGENT='blah X11; Linux Ubuntu/10.04 blah blah')
241+
242+ self.assertRedirects(
243+ response, reverse(
244+ 'wc-package-detail', args=['pkgfoo', 'lucid']))
245+
246+
247 class SearchTestCase(TestCaseWithFactory):
248 def test_no_search_retrieves_no_apps(self):
249 # Ensure there's some data in the DB
250
251=== modified file 'src/webcatalog/urls.py'
252--- src/webcatalog/urls.py 2011-06-28 15:03:12 +0000
253+++ src/webcatalog/urls.py 2011-06-29 09:42:34 +0000
254@@ -23,8 +23,6 @@
255 )
256 from django.conf.urls.defaults import patterns, url
257
258-from webcatalog.views import application_detail, application_detail_no_distro
259-
260 __metaclass__ = type
261 __all__ = [
262 'urlpatterns',
263@@ -35,9 +33,9 @@
264 url(r'^$', 'index', name='wc-index'),
265 url(r'^department/(?P<dept_id>\d+)/$', 'department_overview',
266 name='wc-department'),
267- url(r'^applications/(?P<distro>[-.+\w]+)/(?P<slug>[-.+\w]+)/$',
268- application_detail, name="wc-package-detail"),
269- url(r'^applications/(?P<slug>[-.+\w]+)/$', application_detail_no_distro,
270- name="wc-package-detail-no-distro"),
271+ url(r'^applications/(?P<package_name>[-.+\w]+)/(?P<distro>[-.+\w]+)/$',
272+ 'application_detail', name="wc-package-detail"),
273+ url(r'^applications/(?P<package_name>[-.+\w]+)/$', 'application_detail',
274+ name="wc-package-detail"),
275 url(r'^search/$', 'search', name="wc-search"),
276 )
277
278=== modified file 'src/webcatalog/views.py'
279--- src/webcatalog/views.py 2011-06-28 18:31:28 +0000
280+++ src/webcatalog/views.py 2011-06-29 09:42:34 +0000
281@@ -29,6 +29,7 @@
282 from django.core.paginator import Paginator
283 from django.core.urlresolvers import reverse
284 from django.db.models import Q
285+from django.http import HttpResponseRedirect
286 from django.template import RequestContext
287 from django.shortcuts import (
288 get_object_or_404,
289@@ -107,67 +108,42 @@
290 context_instance=context)
291
292
293-def application_detail_no_distro(request, slug):
294- default_distro = settings.DEFAULT_DISTRO
295- return application_detail(request, default_distro, slug)
296-
297-
298-def application_detail(request, distro, slug):
299- os = get_user_os(request)
300- app = get_object_or_404(Application, package_name=slug,
301+def application_detail(request, package_name, distro=None):
302+ os = get_user_os(request.META.get('HTTP_USER_AGENT', ''))
303+ if distro is None:
304+ # Check for the distroseries in the useragent, if we have it,
305+ # redirect there.
306+ if not os['distro_is_a_guess'] and os['distro']:
307+ distro = os['distro']
308+ else:
309+ distro = settings.DEFAULT_DISTRO
310+ return HttpResponseRedirect(
311+ reverse('wc-package-detail',
312+ args=[package_name, distro]))
313+
314+ app = get_object_or_404(Application, package_name=package_name,
315 distroseries__code_name=distro)
316- button_text = 'Purchase' if app.for_purchase else 'Install'
317- right_platform = os['is_linux']
318- right_arch = app.architectures.find(os['arch']) > -1
319- right_distro = os['distro'].find(app.distroseries.code_name) > -1
320- perfect_match = (right_platform and right_distro and right_arch)
321- message_text = 'Not available for your version of Ubuntu'
322- links = []
323- if not perfect_match:
324- if not right_platform:
325- message_text = "Not available for your platform"
326- links.append(('Download Ubuntu', 'http://www.ubuntu.com/download'))
327- elif not right_distro and os['distro_is_a_guess']:
328- # see if we have this package for *other* distros
329- apps_found = Application.objects.filter(package_name=slug).exclude(
330- distroseries__code_name=os['distro'])
331- if apps_found:
332- message_text = ('Not running %s?' % os['distro'])
333- for app_found in apps_found:
334- args = [app.distroseries.code_name, app.package_name]
335- url = reverse('wc-package-detail', args=args)
336- text = "%s %s for %s" % (button_text, app.name,
337- app_found.distroseries.code_name)
338- links.append((text, url))
339- elif not right_distro:
340- app_found = Application.objects.filter(package_name=slug,
341- distroseries__code_name=os['distro'])
342- if app_found:
343- args = [app.distroseries.code_name, app.package_name]
344- url = reverse('wc-package-detail', args=args)
345- message_text = ('%s is also available for your version '
346- 'of Ubuntu' % app.name)
347- links.append(("%s for os['distro']" % button_text, url))
348-
349- ctx = {'application': app, 'button_text': button_text,
350- 'hide_button': not perfect_match, 'message_text': message_text,
351- 'breadcrumbs': app.crumbs(), 'links': links}
352-
353- context = RequestContext(request, dict=ctx)
354- return render_to_response('webcatalog/application_detail.html', context)
355-
356-
357-def get_user_os(request):
358+
359+ return render_to_response(
360+ 'webcatalog/application_detail.html', RequestContext(
361+ request, dict(application=app, breadcrumbs=app.crumbs())))
362+
363+
364+def get_user_os(user_agent_string):
365 distros = {
366 '10.04': 'lucid',
367 '10.10': 'maverick',
368 '11.04': 'natty',
369 '11.10': 'oneiric',
370 }
371- os = {'is_linux': False, 'distro': 'unknown', 'arch': 'unknown'}
372- try:
373- ua = request.META['HTTP_USER_AGENT']
374- except KeyError:
375+ os = {
376+ 'is_linux': False,
377+ 'distro': '',
378+ 'arch': '',
379+ 'distro_is_a_guess': False,
380+ }
381+ ua = user_agent_string
382+ if not ua:
383 return os
384 os['is_linux'] = ua.find('X11; Linux') > -1
385 if os['is_linux']:

Subscribers

People subscribed via source and target branches