Merge lp:~zematynnad/ubuntu-webcatalog/banners_965560 into lp:ubuntu-webcatalog

Proposed by Danny Tamez
Status: Merged
Approved by: Danny Tamez
Approved revision: 104
Merged at revision: 101
Proposed branch: lp:~zematynnad/ubuntu-webcatalog/banners_965560
Merge into: lp:ubuntu-webcatalog
Diff against target: 291 lines (+186/-6)
7 files modified
src/webcatalog/management/commands/import_exhibits.py (+2/-0)
src/webcatalog/migrations/0020_weight_to_exhibit.py (+164/-0)
src/webcatalog/models/applications.py (+1/-0)
src/webcatalog/tests/factory.py (+2/-2)
src/webcatalog/tests/test_commands.py (+3/-1)
src/webcatalog/tests/test_views.py (+13/-0)
src/webcatalog/views.py (+1/-3)
To merge this branch: bzr merge lp:~zematynnad/ubuntu-webcatalog/banners_965560
Reviewer Review Type Date Requested Status
Michael Nelson (community) Approve
Review via email: mp+101977@code.launchpad.net

Commit message

Adds ordering of the exhibits in the exhibits banner

Description of the change

Overview
========
This branch fixes the ordering of the banners displayed in the exhibits widget.

Details
========
The weight field which is used for ordering the exhibits in sca is now being brought over in the import_exhibits command. The exhibits are now ordered by that field and their order is no longer randomized in the view.

To Test
=======
$fab bootstrap test

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

Nice and straight-forward - thanks Danny!

No need to change, but just if it's helpful information - you can actually grab things out of the response context for you test... in this case you could grab response.context['exhibits'] (or whatever it is in the context) and check the ordering directly, rather than having to search and compare indexes in the response html. That said, your current test is more end-to-end, as a template tag could always be used to reverse the order or whatever. Just food for thought:)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/management/commands/import_exhibits.py'
2--- src/webcatalog/management/commands/import_exhibits.py 2012-03-01 21:38:31 +0000
3+++ src/webcatalog/management/commands/import_exhibits.py 2012-04-13 21:23:18 +0000
4@@ -56,6 +56,8 @@
5 for field in ['package_names', 'html', 'banner_url']:
6 args[field] = exhibit.get(field, '')
7
8+ args['weight'] = exhibit.get('weight', 0)
9+
10 # Munge banner_url into html
11 banner_url = args['banner_url']
12 if banner_url:
13
14=== added file 'src/webcatalog/migrations/0020_weight_to_exhibit.py'
15--- src/webcatalog/migrations/0020_weight_to_exhibit.py 1970-01-01 00:00:00 +0000
16+++ src/webcatalog/migrations/0020_weight_to_exhibit.py 2012-04-13 21:23:18 +0000
17@@ -0,0 +1,164 @@
18+# -*- coding: utf-8 -*-
19+import datetime
20+from south.db import db
21+from south.v2 import SchemaMigration
22+from django.db import models
23+
24+
25+class Migration(SchemaMigration):
26+
27+ def forwards(self, orm):
28+ # Adding field 'Exhibit.weight'
29+ db.add_column('webcatalog_exhibit', 'weight',
30+ self.gf('django.db.models.fields.IntegerField')(default=0),
31+ keep_default=False)
32+
33+ def backwards(self, orm):
34+ # Deleting field 'Exhibit.weight'
35+ db.delete_column('webcatalog_exhibit', 'weight')
36+
37+ models = {
38+ 'auth.group': {
39+ 'Meta': {'object_name': 'Group'},
40+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
41+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
42+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
43+ },
44+ 'auth.permission': {
45+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
46+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
47+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
48+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
50+ },
51+ 'auth.user': {
52+ 'Meta': {'object_name': 'User'},
53+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
54+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
55+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
56+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
57+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
58+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
59+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
60+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
61+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
62+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
63+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
64+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
65+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
66+ },
67+ 'contenttypes.contenttype': {
68+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
69+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
70+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
71+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
72+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
73+ },
74+ 'webcatalog.application': {
75+ 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'},
76+ 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
77+ 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
78+ 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
79+ 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
80+ 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
81+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
82+ 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
83+ 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
84+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
85+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
86+ 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
87+ 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
88+ 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
89+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
90+ 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
91+ 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
92+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
93+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
94+ 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
95+ 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
96+ 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
97+ 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
98+ 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
99+ 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
100+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
101+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
102+ 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
103+ },
104+ 'webcatalog.applicationmedia': {
105+ 'Meta': {'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
106+ 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
107+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
109+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
110+ },
111+ 'webcatalog.consumer': {
112+ 'Meta': {'object_name': 'Consumer'},
113+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
114+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
115+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
116+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'nDcywpUpPYwdIckXOsrVjMBKQwMuSk'", 'max_length': '255', 'blank': 'True'}),
117+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
118+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
119+ },
120+ 'webcatalog.department': {
121+ 'Meta': {'object_name': 'Department'},
122+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
124+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
125+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
126+ },
127+ 'webcatalog.distroseries': {
128+ 'Meta': {'object_name': 'DistroSeries'},
129+ 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
130+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
131+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
132+ },
133+ 'webcatalog.exhibit': {
134+ 'Meta': {'object_name': 'Exhibit'},
135+ 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
136+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
137+ 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
138+ 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
139+ 'html': ('django.db.models.fields.TextField', [], {}),
140+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
141+ 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
142+ 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
143+ 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
144+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
145+ },
146+ 'webcatalog.machine': {
147+ 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
148+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
149+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
150+ 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
151+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
152+ 'package_list': ('django.db.models.fields.TextField', [], {}),
153+ 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
154+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
155+ },
156+ 'webcatalog.nonce': {
157+ 'Meta': {'object_name': 'Nonce'},
158+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
159+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
160+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
161+ 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
162+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
163+ },
164+ 'webcatalog.reviewstatsimport': {
165+ 'Meta': {'object_name': 'ReviewStatsImport'},
166+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
167+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
168+ 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
169+ },
170+ 'webcatalog.token': {
171+ 'Meta': {'object_name': 'Token'},
172+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
173+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
174+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
175+ 'token': ('django.db.models.fields.CharField', [], {'default': "'uKqOdpKariCfWFVhuVQbAYOknPhTELPfpvfZVNdKeapBClaiiU'", 'max_length': '50', 'primary_key': 'True'}),
176+ 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'QkGeNWgqkohZyvBVCcMGtHDiNoSmSoxUxjlblBvjJbEZTmyAof'", 'max_length': '50'}),
177+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
178+ }
179+ }
180+
181+ complete_apps = ['webcatalog']
182\ No newline at end of file
183
184=== modified file 'src/webcatalog/models/applications.py'
185--- src/webcatalog/models/applications.py 2012-04-05 01:09:37 +0000
186+++ src/webcatalog/models/applications.py 2012-04-13 21:23:18 +0000
187@@ -255,6 +255,7 @@
188 display = models.NullBooleanField(
189 help_text="Yes: Always display. No: Never display. "
190 "Unknown: Display if published")
191+ weight = models.IntegerField(default=0)
192
193 class Meta:
194 app_label = 'webcatalog'
195
196=== modified file 'src/webcatalog/tests/factory.py'
197--- src/webcatalog/tests/factory.py 2012-03-23 22:41:16 +0000
198+++ src/webcatalog/tests/factory.py 2012-04-13 21:23:18 +0000
199@@ -130,7 +130,7 @@
200 return Department.objects.create(name=name, parent=parent, slug=slug)
201
202 def make_exhibit(self, package_names=None, published=True, display=None,
203- distroseries=None):
204+ distroseries=None, weight=0):
205 sca_id = self.get_unique_integer()
206 if package_names is None:
207 package_names = self.get_unique_string(prefix='package-')
208@@ -140,7 +140,7 @@
209 distroseries = self.make_distroseries()
210 xibit = Exhibit.objects.create(sca_id=sca_id,
211 package_names=package_names, banner_url=banner_url, html=html,
212- published=published, display=display)
213+ published=published, display=display, weight=weight)
214 xibit.distroseries.add(distroseries)
215 return xibit
216
217
218=== modified file 'src/webcatalog/tests/test_commands.py'
219--- src/webcatalog/tests/test_commands.py 2012-04-03 17:45:07 +0000
220+++ src/webcatalog/tests/test_commands.py 2012-04-13 21:23:18 +0000
221@@ -684,6 +684,7 @@
222 'code_name': ds.code_name,
223 'html': exhibit.html,
224 'date_created': str(exhibit.date_created),
225+ 'weight': str(exhibit.weight),
226 'id': exhibit.sca_id})
227 if changes:
228 for atts, change in zip(data, changes):
229@@ -713,7 +714,7 @@
230 new_html = self.factory.get_unique_string(prefix='new-')
231 mock_urlopen.return_value = self.mock_response([xibit], changes=[
232 {'package_names': new_package_names, 'banner_url': new_banner_url,
233- 'html': new_html}])
234+ 'html': new_html, 'weight': 3}])
235
236 call_command('import_exhibits')
237
238@@ -722,6 +723,7 @@
239 self.assertEqual(new_package_names, retrieved.package_names)
240 self.assertEqual(new_banner_url, retrieved.banner_url)
241 self.assertEqual(new_html, retrieved.html)
242+ self.assertEqual(3, retrieved.weight)
243
244 @patch('urllib.urlopen')
245 def test_creates_exhibit_if_scaid_does_not_match(self, mock_urlopen):
246
247=== modified file 'src/webcatalog/tests/test_views.py'
248--- src/webcatalog/tests/test_views.py 2012-03-30 13:42:37 +0000
249+++ src/webcatalog/tests/test_views.py 2012-04-13 21:23:18 +0000
250@@ -569,6 +569,19 @@
251
252 self.assertContains(response, '<li class="slide', count=1)
253
254+ def test_exhibits_orders_banners_by_weight(self):
255+ third = self.factory.make_exhibit(display=True, weight=3)
256+ second = self.factory.make_exhibit(display=True, weight=2)
257+ first = self.factory.make_exhibit(display=True, weight=1)
258+
259+ response = self.client.get(reverse('wc-index'))
260+
261+ first_pos = response.content.index(first.destination_url())
262+ second_pos = response.content.index(second.destination_url())
263+ third_pos = response.content.index(third.destination_url())
264+
265+ self.assertTrue(third_pos > second_pos > first_pos)
266+
267 def test_featured_apps(self):
268 app = self.factory.make_application(package_name='foobar')
269
270
271=== modified file 'src/webcatalog/views.py'
272--- src/webcatalog/views.py 2012-04-05 01:09:37 +0000
273+++ src/webcatalog/views.py 2012-04-13 21:23:18 +0000
274@@ -25,7 +25,6 @@
275 import json
276 import operator
277 import os
278-from random import shuffle
279
280 from convoy.combo import combine_files, parse_qs
281 from django.conf import settings
282@@ -112,8 +111,7 @@
283 depts = Department.objects.filter(parent=None).order_by('name')
284 depts = depts.order_by('name')
285 exhibits = list(Exhibit.objects.filter(Q(display=True) |
286- Q(display=None, published=True,)))
287- shuffle(exhibits)
288+ Q(display=None, published=True,)).order_by('weight'))
289 featured_apps = [Application.objects.find_best(package_name=app)
290 for app in settings.FEATURED_APPS]
291 featured_apps = [x for x in featured_apps if x]

Subscribers

People subscribed via source and target branches