Merge lp:~zematynnad/ubuntu-webcatalog/banners_965560 into lp:ubuntu-webcatalog
- banners_965560
- Merge into trunk
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 |
Related bugs: |
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.
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] |
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:)