Merge lp:~michael.nelson/ubuntu-webcatalog/1015505-extras-not-propietary into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Łukasz Czyżykowski
Approved revision: 156
Merged at revision: 151
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/1015505-extras-not-propietary
Merge into: lp:ubuntu-webcatalog
Diff against target: 352 lines (+215/-25)
7 files modified
src/webcatalog/migrations/0023_add_license.py (+168/-0)
src/webcatalog/models/applications.py (+4/-4)
src/webcatalog/templates/webcatalog/install_options_snippet.html (+1/-1)
src/webcatalog/tests/factory.py (+2/-2)
src/webcatalog/tests/test_commands.py (+1/-0)
src/webcatalog/tests/test_data/sca_apps.txt (+2/-1)
src/webcatalog/tests/test_views.py (+37/-17)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/1015505-extras-not-propietary
Reviewer Review Type Date Requested Status
Łukasz Czyżykowski Pending
Review via email: mp+112030@code.launchpad.net

Commit message

Display correct license (from import) on details page.

Description of the change

Overview
========

As per the pre-imp discussion on bug 1015505, this branch adds the Application.license field, and updates the license_type property to be based on that instead of the somewhat confusing Application.for_purchase field.

I also update the template so that it displays a 'buy' button when the price is non-zero, rather than depending on the 'for_purchase' field.

`fab test`

We'll rename for_purchase for bug 1015515 to 'imported_from_sca', as it will need to be used used to determine if the import script should update data.

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=== added file 'src/webcatalog/migrations/0023_add_license.py'
2--- src/webcatalog/migrations/0023_add_license.py 1970-01-01 00:00:00 +0000
3+++ src/webcatalog/migrations/0023_add_license.py 2012-06-26 09:42:20 +0000
4@@ -0,0 +1,168 @@
5+# encoding: utf-8
6+import datetime
7+from south.db import db
8+from south.v2 import SchemaMigration
9+from django.db import models
10+
11+class Migration(SchemaMigration):
12+
13+ def forwards(self, orm):
14+
15+ # Adding field 'Application.license'
16+ db.add_column('webcatalog_application', 'license', self.gf('django.db.models.fields.CharField')(default='', max_length=64, blank=True), keep_default=False)
17+
18+
19+ def backwards(self, orm):
20+
21+ # Deleting field 'Application.license'
22+ db.delete_column('webcatalog_application', 'license')
23+
24+
25+ models = {
26+ 'auth.group': {
27+ 'Meta': {'object_name': 'Group'},
28+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
29+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
30+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
31+ },
32+ 'auth.permission': {
33+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
34+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
35+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
36+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
37+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
38+ },
39+ 'auth.user': {
40+ 'Meta': {'object_name': 'User'},
41+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
42+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
43+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
44+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
45+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
46+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
47+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
48+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
49+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
50+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
51+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
52+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
53+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
54+ },
55+ 'contenttypes.contenttype': {
56+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
57+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
58+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
60+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
61+ },
62+ 'webcatalog.application': {
63+ 'Meta': {'ordering': "('-wilson_score', 'name')", 'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'},
64+ 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
65+ 'application_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
66+ 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
67+ 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
68+ 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
69+ 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
70+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
71+ 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
72+ 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
73+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
74+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
75+ 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
76+ 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
77+ 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
78+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
79+ 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
80+ 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
81+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
82+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
83+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
84+ 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
85+ 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
86+ 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
87+ 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
88+ 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
89+ 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
90+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
91+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
92+ 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
93+ },
94+ 'webcatalog.applicationmedia': {
95+ 'Meta': {'ordering': "('url',)", 'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
96+ 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
97+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
98+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
99+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
100+ },
101+ 'webcatalog.consumer': {
102+ 'Meta': {'object_name': 'Consumer'},
103+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
104+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
105+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
106+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'DpYRwsRbRiwFRbElWgbhSktlpldyjT'", 'max_length': '255', 'blank': 'True'}),
107+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
108+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
109+ },
110+ 'webcatalog.department': {
111+ 'Meta': {'object_name': 'Department'},
112+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
114+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
115+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
116+ },
117+ 'webcatalog.distroseries': {
118+ 'Meta': {'object_name': 'DistroSeries'},
119+ 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
120+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
121+ 'prerelease': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
122+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
123+ },
124+ 'webcatalog.exhibit': {
125+ 'Meta': {'object_name': 'Exhibit'},
126+ 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
127+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
128+ 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
129+ 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
130+ 'html': ('django.db.models.fields.TextField', [], {}),
131+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132+ 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
133+ 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
134+ 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
135+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
136+ },
137+ 'webcatalog.machine': {
138+ 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
139+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
140+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
141+ 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
142+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
143+ 'package_list': ('django.db.models.fields.TextField', [], {}),
144+ 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
145+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
146+ },
147+ 'webcatalog.nonce': {
148+ 'Meta': {'object_name': 'Nonce'},
149+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
150+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
151+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
152+ 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
153+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
154+ },
155+ 'webcatalog.reviewstatsimport': {
156+ 'Meta': {'object_name': 'ReviewStatsImport'},
157+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
158+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159+ 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
160+ },
161+ 'webcatalog.token': {
162+ 'Meta': {'object_name': 'Token'},
163+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
164+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
165+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
166+ 'token': ('django.db.models.fields.CharField', [], {'default': "'IXcKPvbbhtCyTSqNVxRwfbaHLczIBwpjofnpaaGKLcnQgLWwBL'", 'max_length': '50', 'primary_key': 'True'}),
167+ 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'eAJdlxCXakKtlHCevxWppBNjLyRSuilBBVbhanfmpXeqhpRBLZ'", 'max_length': '50'}),
168+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
169+ }
170+ }
171+
172+ complete_apps = ['webcatalog']
173
174=== modified file 'src/webcatalog/models/applications.py'
175--- src/webcatalog/models/applications.py 2012-06-21 11:40:36 +0000
176+++ src/webcatalog/models/applications.py 2012-06-26 09:42:20 +0000
177@@ -102,6 +102,9 @@
178 is_latest = models.BooleanField()
179 wilson_score = models.FloatField(null=True, blank=True, db_index=True)
180 debtags = models.CharField(max_length=255, blank=True)
181+ license = models.CharField(
182+ max_length=64, blank=True,
183+ help_text=u"The name of the license used for the app.")
184
185 # Other desktop fields used by s-c
186 # x-gnome-fullname
187@@ -174,10 +177,7 @@
188 return crumbs
189
190 def license_type(self):
191- if self.for_purchase:
192- return "Proprietary"
193- else:
194- return "Open Source"
195+ return self.license or "Open Source"
196
197 @property
198 def architectures_list(self):
199
200=== modified file 'src/webcatalog/templates/webcatalog/install_options_snippet.html'
201--- src/webcatalog/templates/webcatalog/install_options_snippet.html 2012-03-13 16:25:34 +0000
202+++ src/webcatalog/templates/webcatalog/install_options_snippet.html 2012-06-26 09:42:20 +0000
203@@ -7,7 +7,7 @@
204 {% endif %}
205 {% if display_install_button %}
206 <a href="apt://{{ application.package_name }}">
207- {% if application.for_purchase %}
208+ {% if application.price %}
209 <img class="fancy-install" width="200" height="60" alt="" src="{{ STATIC_URL }}images/scbutton-non-free-200px.png" title="Buy it from the Ubuntu Software Centre!">
210 {% else %}
211 <img class="fancy-install" width="200" height="60" alt="" src="{{ STATIC_URL }}images/scbutton-free-200px.png" title="Get it from the Ubuntu Software Centre!">
212
213=== modified file 'src/webcatalog/tests/factory.py'
214--- src/webcatalog/tests/factory.py 2012-06-21 11:40:36 +0000
215+++ src/webcatalog/tests/factory.py 2012-06-26 09:42:20 +0000
216@@ -104,7 +104,7 @@
217 ratings_histogram='', screenshot_url='',
218 archive_id=None, version='', is_latest=False,
219 wilson_score=0.0, debtags=[], application_id=None,
220- departments=None):
221+ departments=None, license='', price=None):
222 if name is None:
223 name = self.get_unique_string(prefix='Readable Name')
224 if package_name is None:
225@@ -124,7 +124,7 @@
226 ratings_histogram=ratings_histogram,
227 archive_id=archive_id, version=version, is_latest=is_latest,
228 wilson_score=wilson_score, debtags=debtags,
229- application_id=application_id)
230+ application_id=application_id, license=license, price=price)
231
232 if departments is not None:
233 for d in departments:
234
235=== modified file 'src/webcatalog/tests/test_commands.py'
236--- src/webcatalog/tests/test_commands.py 2012-06-21 20:18:44 +0000
237+++ src/webcatalog/tests/test_commands.py 2012-06-26 09:42:20 +0000
238@@ -587,6 +587,7 @@
239 distroseries=self.natty)
240 self.assertEqual(True, app_for_purchase.for_purchase)
241 self.assertTrue(app_for_purchase.description.find('hello') > -1)
242+ self.assertEqual("Proprietary", app_for_purchase.license)
243
244 def test_app_gets_distroseries(self):
245 with patch_settings(UBUNTU_SERIES_FOR_VERSIONS=TEST_VERSIONS):
246
247=== modified file 'src/webcatalog/tests/test_data/sca_apps.txt'
248--- src/webcatalog/tests/test_data/sca_apps.txt 2012-05-03 12:13:00 +0000
249+++ src/webcatalog/tests/test_data/sca_apps.txt 2012-06-26 09:42:20 +0000
250@@ -18,7 +18,8 @@
251 "tos_url": "",
252 "icon_url": "http://localhost:8000/site_media/icons/2011/06/eg_64x64.png",
253 "categories": "Audio",
254- "name": "MyApp"
255+ "name": "MyApp",
256+ "license": "Proprietary"
257 },
258 {
259 "package_name": "eg_arb_app",
260
261=== modified file 'src/webcatalog/tests/test_views.py'
262--- src/webcatalog/tests/test_views.py 2012-06-21 11:44:00 +0000
263+++ src/webcatalog/tests/test_views.py 2012-06-26 09:42:20 +0000
264@@ -76,6 +76,18 @@
265 return reverse('wc-package-detail',
266 args=[app.distroseries.code_name, app.package_name])
267
268+ def get_package_details_response(self, package_name,
269+ useragent=UBUNTU_USERAGENT, distro=None):
270+ kwargs = dict(package_name=package_name)
271+ if distro:
272+ kwargs['distro'] = distro
273+ url = reverse('wc-package-detail', kwargs=kwargs)
274+
275+ if useragent:
276+ return self.client.get(url, HTTP_USER_AGENT=useragent)
277+ else:
278+ return self.client.get(url)
279+
280 def get_app_and_response(self, code_name='natty', version='11.04',
281 arch='x86_64', name=None, comment=None,
282 description=None, detail_distro=None,
283@@ -96,13 +108,8 @@
284 if not detail_package:
285 detail_package = app.package_name
286
287- url = reverse('wc-package-detail',
288- args=[detail_distro, detail_package])
289-
290- if useragent:
291- response = self.client.get(url, HTTP_USER_AGENT=useragent)
292- else:
293- response = self.client.get(url)
294+ response = self.get_package_details_response(
295+ detail_package, distro=detail_distro, useragent=useragent)
296 return response, app
297
298 def test_renders_correct_template(self):
299@@ -160,14 +167,20 @@
300 self.assertNotContains(response, '<div class="recommendations">')
301
302 def test_button_for_non_puchase_app(self):
303- response, app = self.get_app_and_response()
304+ self.factory.make_application(package_name='pkgfoo',
305+ price=None)
306+
307+ response = self.get_package_details_response('pkgfoo')
308
309 self.assertContains(response, '/assets/images/scbutton-free-200px.png')
310 self.assertNotContains(response,
311 '/assets/images/scbutton-non-free-200px.png')
312
313 def test_button_for_for_puchase_app(self):
314- response, app = self.get_app_and_response(for_purchase=True)
315+ self.factory.make_application(package_name='pkgfoo',
316+ price=Decimal('12.99'))
317+
318+ response = self.get_package_details_response('pkgfoo')
319
320 self.assertNotContains(response,
321 '/assets/images/scbutton-free-200px.png')
322@@ -284,15 +297,22 @@
323 self.assertContains(response, "Ubuntu Quantal")
324
325 def test_includes_right_license_type(self):
326- response, app = self.get_app_and_response(for_purchase=False)
327+ self.factory.make_application(license="Foo GNL V1",
328+ package_name='pkgfoo')
329+
330+ response = self.client.get(
331+ reverse('wc-package-detail', args=['pkgfoo']))
332+
333+ self.assertContains(response, '<td>Foo GNL V1</td>')
334+
335+ def test_no_license_assumes_OSS(self):
336+ self.factory.make_application(license="",
337+ package_name='pkgfoo')
338+
339+ response = self.client.get(
340+ reverse('wc-package-detail', args=['pkgfoo']))
341+
342 self.assertContains(response, '<td>Open Source</td>')
343- self.assertNotContains(response, '<td>Proprietary</td>')
344-
345- app.for_purchase = True
346- app.save()
347- response = self.client.get(self.get_app_details_url(app))
348- self.assertContains(response, '<td>Proprietary</td>')
349- self.assertNotContains(response, '<td>Open Source</td>')
350
351 def test_response_includes_rating_summary(self):
352 distro = self.factory.make_distroseries(code_name='lucid')

Subscribers

People subscribed via source and target branches