Merge lp:~michael.nelson/ubuntu-webcatalog/978000-recommended-apps-2 into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Anthony Lenton
Approved revision: 120
Merged at revision: 111
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/978000-recommended-apps-2
Merge into: lp:ubuntu-webcatalog
Prerequisite: lp:~michael.nelson/ubuntu-webcatalog/978000-recommended-apps
Diff against target: 331 lines (+91/-63)
12 files modified
django_project/config/main.cfg (+1/-1)
src/webcatalog/context_processors.py (+9/-9)
src/webcatalog/schema.py (+1/-0)
src/webcatalog/static/css/carousel.css (+2/-2)
src/webcatalog/static/css/webcatalog.css (+6/-2)
src/webcatalog/templates/webcatalog/application_detail.html (+26/-9)
src/webcatalog/templates/webcatalog/recommended_apps.html (+12/-0)
src/webcatalog/templates/webcatalog/recommended_apps_widget.html (+18/-20)
src/webcatalog/tests/test_utilities.py (+2/-2)
src/webcatalog/tests/test_views.py (+11/-16)
src/webcatalog/utilities.py (+2/-1)
src/webcatalog/views.py (+1/-1)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/978000-recommended-apps-2
Reviewer Review Type Date Requested Status
Anthony Lenton (community) Approve
Review via email: mp+102709@code.launchpad.net

Commit message

Add recommended apps to the details page side-bar.

Description of the change

Overview
========

Originally I'd planned to have the recommendations in a carousel like this:

https://bugs.launchpad.net/ubuntu-webcatalog/+bug/978000/+attachment/3094678/+files/978000-app-with-recommendations.png

(hence the work on dry-ing up the carousel templates), but after chatting, we decided to put them in the side bar like this:

https://bugs.launchpad.net/ubuntu-webcatalog/+bug/978000/+attachment/3094789/+files/978000-recommended-sidebar.png

which is what this branch now does.

I decided not to include a default link for non-js, as it would be ugly in the side-bar, and the functionality of adding the recommendations is a progressive enhancement anyway (and not required to use the page).

`fab test`

NOTE: I've modified the name of a context_processor below and updated main.cfg, but I've not yet checked if our settings for deploy inherit from this main.cfg, or whether we need to make the same change there.

NOTE2: I just realised we'll need to provide the recommender api (sreclient.py) in the deploy and afaik it's not yet packaged. The previous branch pulls it in via bzr. Let me know the best way forward and I'll do it in the morning.

To post a comment you must log in.
120. By Michael Nelson

Removed now unnecessary js import.

Revision history for this message
Anthony Lenton (elachuni) wrote :

Looks fine

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 2012-04-18 22:29:29 +0000
3+++ django_project/config/main.cfg 2012-04-19 23:28:18 +0000
4@@ -49,8 +49,8 @@
5 django.core.context_processors.media
6 django.core.context_processors.static
7 django.contrib.messages.context_processors.messages
8- webcatalog.context_processors.google_analytics_id
9 webcatalog.context_processors.user_agent
10+ webcatalog.context_processors.settings_for_templates
11
12 static_root = ./django_project/static/
13 static_url = /assets/
14
15=== modified file 'src/webcatalog/context_processors.py'
16--- src/webcatalog/context_processors.py 2012-03-07 22:57:41 +0000
17+++ src/webcatalog/context_processors.py 2012-04-19 23:28:18 +0000
18@@ -20,15 +20,15 @@
19 from django.conf import settings
20
21
22-def google_analytics_id(request):
23- """Adds the google analytics id to the context if it's present."""
24- return {
25- 'google_analytics_id': getattr(settings, 'GOOGLE_ANALYTICS_ID', None),
26- 'secondary_google_analytics_id':
27- getattr(settings, 'SECONDARY_GOOGLE_ANALYTICS_ID', None),
28- }
29-
30-
31 def user_agent(request):
32 """Adds the browser user-agent string to the context if present."""
33 return dict(user_agent=request.META.get('HTTP_USER_AGENT', ''))
34+
35+
36+def settings_for_templates(request):
37+ """Adds some settings used in templates so views can know less :)"""
38+ return dict(
39+ num_recommended_apps=settings.NUM_RECOMMENDED_APPS,
40+ google_analytics_id=getattr(settings, 'GOOGLE_ANALYTICS_ID', None),
41+ secondary_google_analytics_id=getattr(settings,
42+ 'SECONDARY_GOOGLE_ANALYTICS_ID', None))
43
44=== modified file 'src/webcatalog/schema.py'
45--- src/webcatalog/schema.py 2012-04-19 23:28:18 +0000
46+++ src/webcatalog/schema.py 2012-04-19 23:28:18 +0000
47@@ -92,3 +92,4 @@
48 class recommender(schema.Section):
49 rec_service_root = schema.StringOption(
50 default="http://rec.ubuntu.com/api/1.0")
51+ num_recommended_apps = schema.IntOption(default=4)
52
53=== modified file 'src/webcatalog/static/css/carousel.css'
54--- src/webcatalog/static/css/carousel.css 2012-04-19 23:28:18 +0000
55+++ src/webcatalog/static/css/carousel.css 2012-04-19 23:28:18 +0000
56@@ -146,7 +146,7 @@
57 border-right: 0;
58 }
59
60-#featured-carousel .pagination li a.active,
61+.featured-widget .pagination li a.active,
62 #top-rated-carousel .pagination li a.active {
63 background-color: #333;
64 }
65@@ -213,7 +213,7 @@
66 bottom: 0;
67 }
68
69-.top-rated-stars {
70+.carousel-container .top-rated-stars {
71 position: relative;
72 left: -50px;
73 top: -15px;
74
75=== modified file 'src/webcatalog/static/css/webcatalog.css'
76--- src/webcatalog/static/css/webcatalog.css 2012-04-05 01:09:37 +0000
77+++ src/webcatalog/static/css/webcatalog.css 2012-04-19 23:28:18 +0000
78@@ -365,9 +365,13 @@
79 border-bottom: 1px dotted gray;
80 }
81
82-.emaillinkportlet .portletheader, .debtags .portletheader, .versions .portletheader {
83+.emaillinkportlet .portletheader, .debtags .portletheader,
84+.versions .portletheader, .recommendedportlet .portletheader {
85 background: #bbb;
86 }
87+.portletactions .star-rating {
88+ padding-top: 10px;
89+}
90
91 #emailForm {
92 margin-top: 8px;
93@@ -422,4 +426,4 @@
94 font-size: 32px;
95 margin: 32px;
96 color: #888;
97-}
98\ No newline at end of file
99+}
100
101=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
102--- src/webcatalog/templates/webcatalog/application_detail.html 2012-04-18 21:27:46 +0000
103+++ src/webcatalog/templates/webcatalog/application_detail.html 2012-04-19 23:28:18 +0000
104@@ -5,21 +5,34 @@
105 {% block title %}Application {{ application.name }}{% endblock %}
106 {% block header %}Details for {{ application.name }}{% endblock %}
107 {% block head_extra %}
108-{{ block.super }}
109 <link rel="stylesheet" type="text/css" href="{% url wc-combo %}?light/css/reset.css&light/css/styles.css&css/webcatalog.css&light/css/forms.css&css/carousel.css"/>
110 <script src="{% url wc-combo %}?yui/3.4.0/build/yui/yui-min.js&js/carousel.js"></script>
111 <script>
112 YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('io-base', 'node-base', function (Y) {
113- function complete(id, obj){
114- var reviews_html = obj.responseText;
115- var reviews_div = Y.one('#reviews_placeholder');
116- reviews_div.set("innerHTML", reviews_html);
117- };
118- Y.on('io:complete', complete, Y);
119
120 // XXX michaeln 2011-09-16 bug=851660 Autoload further batches of results.
121- var uri = "{% url wc-package-reviews application.distroseries.code_name application.package_name %}";
122- Y.io(uri);
123+ var reviews_uri = "{% url wc-package-reviews application.distroseries.code_name application.package_name %}";
124+ Y.io(reviews_uri, {
125+ on: {
126+ complete: function(id, obj){
127+ var reviews_html = obj.responseText;
128+ var reviews_div = Y.one('#reviews_placeholder');
129+ reviews_div.set("innerHTML", reviews_html);
130+ }
131+ }
132+ });
133+ var recommends_div = Y.one('.portlet.recommendedportlet');
134+ if (recommends_div){
135+ var recommendations_uri = "{% url wc-package-recommends application.package_name %}";
136+ Y.io(recommendations_uri, {
137+ on: {
138+ complete: function(id, obj){
139+ var recommends_html = obj.responseText;
140+ recommends_div.set("innerHTML", recommends_html);
141+ }
142+ }
143+ });
144+ }
145 });
146 </script>
147 <meta property="og:site_name" content="Ubuntu Apps Directory" />
148@@ -67,6 +80,10 @@
149 </ul>
150 </div>
151 {% endif %}
152+ {% if num_recommended_apps %}
153+ <div class="portlet recommendedportlet">
154+ </div>
155+ {% endif %}
156 </div>
157 <div class="main-column">
158 {% include "webcatalog/breadcrumbs_snippet.html" %}
159
160=== modified file 'src/webcatalog/templates/webcatalog/recommended_apps.html'
161--- src/webcatalog/templates/webcatalog/recommended_apps.html 2012-04-19 23:28:18 +0000
162+++ src/webcatalog/templates/webcatalog/recommended_apps.html 2012-04-19 23:28:18 +0000
163@@ -23,5 +23,17 @@
164 <div class="featured-widget">
165 {% include "webcatalog/recommended_apps_widget.html" %}
166 </div>
167+<script type="text/javascript">
168+YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('uwc-carousel', function(Y) {
169+ var caro = new Y.uwc.Carousel({
170+ nodeContainer: ".featured-widget .carousel",
171+ controlsContainer: ".featured-widget .carousel-controls",
172+ containerHeight: 200,
173+ containerWidth: 912,
174+ autoPlay: false
175+ });
176+ Y.all('.slide').removeClass('disabled');
177+});
178+</script>
179 {% endblock %}
180
181
182=== modified file 'src/webcatalog/templates/webcatalog/recommended_apps_widget.html'
183--- src/webcatalog/templates/webcatalog/recommended_apps_widget.html 2012-04-19 23:28:18 +0000
184+++ src/webcatalog/templates/webcatalog/recommended_apps_widget.html 2012-04-19 23:28:18 +0000
185@@ -1,20 +1,18 @@
186-<div id="recommended-carousel">
187-<div class="carousel-controls"></div>
188- <h3>Other people also downloaded...</h3>
189-{% with ratings=1 apps=recommended_apps %}
190-{% include "webcatalog/app_carousel_widget.html" %}
191-{% endwith %}
192-</div>
193-<script type="text/javascript">
194-YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('uwc-carousel', function(Y) {
195- var caro = new Y.uwc.Carousel({
196- nodeContainer: "#recommended-carousel .carousel",
197- controlsContainer: "#recommended-carousel .carousel-controls",
198- containerHeight: 200,
199- containerWidth: 912,
200- autoPlay: false
201- });
202- Y.all('.slide').removeClass('disabled');
203-});
204-</script>
205-
206+{% load webcatalog %}
207+<div class="portletheader">Other people also downloaded...</div>
208+<ul class="portletactions">
209+{% for app in recommended_apps %}
210+ <li>
211+ <a href="{% url wc-package-detail package_name=app.package_name %}">
212+ <div class="top-rated-stars">
213+ {% rating_summary app.ratings_average 'small' app.ratings_total %}
214+ </div>
215+ <img class="icon64" src="{{ app.icon_url_or_default }}"/>
216+ <h4>{{ app.name }}</h4>
217+ {% with dept=app.departments.all.0.name %}
218+ <p>{% if dept %}{{ dept }} | {% endif %}<b>{% if app.price %}${{ app.price }}{% else %}FREE{% endif %}</b></p>
219+ {% endwith %}
220+</a>
221+ </li>
222+{% endfor %}
223+</ul>
224
225=== modified file 'src/webcatalog/tests/test_utilities.py'
226--- src/webcatalog/tests/test_utilities.py 2012-04-19 23:28:18 +0000
227+++ src/webcatalog/tests/test_utilities.py 2012-04-19 23:28:18 +0000
228@@ -140,7 +140,7 @@
229 self.mock_recommend_app = self.recommend_app_patcher.start()
230 self.addCleanup(self.recommend_app_patcher.stop)
231 self.eg_recommends = {
232- u'rid': u'defe066c7ad7c43f71bac58c3f23cc62',
233+ u'rid': u'defe066c7ad7c43f71bac58c3f23cc62',
234 u'data': [
235 {u'rating': 4.0, u'package_name': u'nautilus-gksu'},
236 {u'rating': 4.0, u'package_name': u'tribaltrouble2'},
237@@ -161,7 +161,7 @@
238
239 def test_get_recommends_caches_result(self):
240 self.assertIs(None, cache.get('get_recommends_for_package-firefox'))
241-
242+
243 result = WebServices().get_recommends_for_package('firefox')
244
245 self.assertEqual(
246
247=== modified file 'src/webcatalog/tests/test_views.py'
248--- src/webcatalog/tests/test_views.py 2012-04-19 23:28:18 +0000
249+++ src/webcatalog/tests/test_views.py 2012-04-19 23:28:18 +0000
250@@ -143,6 +143,12 @@
251 app.package_name,
252 ])))
253
254+ def test_link_to_recommended_apps_not_present(self):
255+ with patch_settings(NUM_RECOMMENDED_APPS=0):
256+ response, app = self.get_app_and_response()
257+
258+ self.assertNotContains(response, '<div class="recommendations">')
259+
260 def test_button_for_non_puchase_app(self):
261 response, app = self.get_app_and_response()
262
263@@ -334,7 +340,7 @@
264
265 response = self.client.get(self.get_app_details_url(app))
266
267- self.assertContains(response, 'id="screenshots-carousel"')
268+ self.assertContains(response, 'Y.uwc.Carousel', 1)
269
270 def test_debtags_displayed(self):
271 app = self.factory.make_application(debtags='["joystick"]')
272@@ -1085,8 +1091,7 @@
273
274 def test_renders_recommendations(self):
275 app = self.factory.make_application(package_name='firefox')
276- pkgnames = ['nautilus-gksu', 'tribaltrouble2', 'acm', 'zgv',
277- 'nautilus-wallpaper']
278+ pkgnames = ['nautilus-gksu', 'tribaltrouble2', 'acm']
279 for pkgname in pkgnames:
280 self.factory.make_application(package_name=pkgname)
281
282@@ -1096,19 +1101,9 @@
283 self.assertEqual(200, response.status_code)
284 self.assertTemplateUsed(
285 response, 'webcatalog/recommended_apps.html')
286- self.assertContains(response, '<div class="top-rated-stars">', 5)
287-
288- def test_excludes_non_existing_recommendations(self):
289- app = self.factory.make_application(package_name='firefox')
290- pkgnames = ['nautilus-gksu', 'tribaltrouble2', 'acm', 'zgv']
291- for pkgname in pkgnames:
292- self.factory.make_application(package_name=pkgname)
293-
294- response = self.client.get(
295- reverse('wc-package-recommends', args=['firefox']))
296-
297- self.assertEqual(200, response.status_code)
298- self.assertContains(response, '<div class="top-rated-stars">', 4)
299+ # Only recommendations for apps that exist in the database are
300+ # included.
301+ self.assertContains(response, '<div class="top-rated-stars">', 3)
302
303 def test_renders_widget_only_for_json_requests(self):
304 app = self.factory.make_application(package_name='firefox')
305
306=== modified file 'src/webcatalog/utilities.py'
307--- src/webcatalog/utilities.py 2012-04-19 23:28:18 +0000
308+++ src/webcatalog/utilities.py 2012-04-19 23:28:18 +0000
309@@ -166,7 +166,8 @@
310
311 @property
312 def recommender_api(self):
313- return SoftwareCenterRecommenderAPI(service_root=settings.REC_SERVICE_ROOT)
314+ return SoftwareCenterRecommenderAPI(
315+ service_root=settings.REC_SERVICE_ROOT)
316
317
318 def full_claimed_id(consumer_key):
319
320=== modified file 'src/webcatalog/views.py'
321--- src/webcatalog/views.py 2012-04-19 23:28:18 +0000
322+++ src/webcatalog/views.py 2012-04-19 23:28:18 +0000
323@@ -227,7 +227,7 @@
324 if recommended_app:
325 recommended_apps.append(recommended_app)
326 num_recommends += 1
327- if num_recommends > 5:
328+ if num_recommends == settings.NUM_RECOMMENDED_APPS:
329 break
330
331 context = dict(application=app, recommended_apps=recommended_apps)

Subscribers

People subscribed via source and target branches