Merge lp:~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: 199
Merged at revision: 196
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error
Merge into: lp:ubuntu-webcatalog
Diff against target: 254 lines (+191/-3)
5 files modified
src/webcatalog/forms.py (+1/-1)
src/webcatalog/management/commands/import_sca_apps.py (+6/-1)
src/webcatalog/migrations/0030_application_debtags_to_textfield.py (+174/-0)
src/webcatalog/models/applications.py (+1/-1)
src/webcatalog/tests/test_commands.py (+9/-0)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error
Reviewer Review Type Date Requested Status
Martin Albisetti (community) Approve
Review via email: mp+201146@code.launchpad.net

Commit message

Don't error on bad/unexpected import data.
Enable debtags field to be > 255.

Description of the change

Fixes bug 1267731 by:
 1) Ensuring we don't error if one sca app happens to have unexpected data, instead logging the error
 2) Enabling the debtags field to be > 255

To post a comment you must log in.
Revision history for this message
Martin Albisetti (beuno) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/forms.py'
2--- src/webcatalog/forms.py 2013-05-21 12:24:28 +0000
3+++ src/webcatalog/forms.py 2014-01-10 09:19:06 +0000
4@@ -161,7 +161,7 @@
5
6 try:
7 instance = Application.objects.get(
8- package_name=app_data['package_name'],
9+ package_name=app_data.get('package_name'),
10 distroseries=distroseries)
11 except Application.DoesNotExist:
12 instance = None
13
14=== modified file 'src/webcatalog/management/commands/import_sca_apps.py'
15--- src/webcatalog/management/commands/import_sca_apps.py 2013-09-03 08:45:45 +0000
16+++ src/webcatalog/management/commands/import_sca_apps.py 2014-01-10 09:19:06 +0000
17@@ -123,12 +123,17 @@
18 def import_app_from_data(self, app_data, icon_data, distroseries):
19 form = SCAApplicationForm.from_api_data(
20 app_data, distroseries)
21+ package_name = None
22 if form.is_valid():
23 app = form.save()
24 department_names = app_data.get('department', [])
25 app.update_departments(department_names)
26+ package_name = app.package_name
27 self.add_icon_to_app(app, data=icon_data)
28- return app.package_name
29+ else:
30+ logger.error("An SCA app failed to import. Errors: %s",
31+ form.errors)
32+ return package_name
33
34 def get_icon_data(self, app_data):
35 icon_data = app_data.get('icon_data', '')
36
37=== added file 'src/webcatalog/migrations/0030_application_debtags_to_textfield.py'
38--- src/webcatalog/migrations/0030_application_debtags_to_textfield.py 1970-01-01 00:00:00 +0000
39+++ src/webcatalog/migrations/0030_application_debtags_to_textfield.py 2014-01-10 09:19:06 +0000
40@@ -0,0 +1,174 @@
41+# -*- coding: utf-8 -*-
42+import datetime
43+from south.db import db
44+from south.v2 import SchemaMigration
45+from django.db import models
46+
47+
48+class Migration(SchemaMigration):
49+
50+ def forwards(self, orm):
51+
52+ # Changing field 'Application.debtags'
53+ db.alter_column(u'webcatalog_application', 'debtags', self.gf('django.db.models.fields.TextField')())
54+
55+ def backwards(self, orm):
56+
57+ # Changing field 'Application.debtags'
58+ db.alter_column(u'webcatalog_application', 'debtags', self.gf('django.db.models.fields.CharField')(max_length=255))
59+
60+ models = {
61+ u'auth.group': {
62+ 'Meta': {'object_name': 'Group'},
63+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
64+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
65+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
66+ },
67+ u'auth.permission': {
68+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
69+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
70+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
71+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
73+ },
74+ u'auth.user': {
75+ 'Meta': {'object_name': 'User'},
76+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
77+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
78+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
79+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
80+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
81+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
82+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
83+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
84+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
85+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
86+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
87+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
88+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
89+ },
90+ u'contenttypes.contenttype': {
91+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
92+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
93+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
94+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
95+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
96+ },
97+ 'webcatalog.application': {
98+ 'Meta': {'ordering': "('-wilson_score', 'name')", 'unique_together': "(('distroseries', 'package_name'),)", 'object_name': 'Application'},
99+ 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
100+ 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
101+ 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
102+ 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
103+ 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
104+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
105+ 'debtags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
106+ 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
107+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
108+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
109+ 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
110+ 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
111+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
112+ 'imported_from_sca': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
113+ 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
114+ 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
115+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
116+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
117+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
118+ 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
119+ 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
120+ 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
121+ 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
122+ 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
123+ 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
124+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
125+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
126+ 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
127+ },
128+ 'webcatalog.applicationmedia': {
129+ 'Meta': {'ordering': "('url',)", 'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
130+ 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
131+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
133+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
134+ },
135+ 'webcatalog.applicationwidget': {
136+ 'Meta': {'object_name': 'ApplicationWidget'},
137+ 'applications': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Application']", 'symmetrical': 'False'}),
138+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
140+ 'template_snippet': ('django.db.models.fields.TextField', [], {})
141+ },
142+ 'webcatalog.consumer': {
143+ 'Meta': {'object_name': 'Consumer'},
144+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
145+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
146+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
147+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'eOaKTZKZvdBXcmqOogpjVgwahoZiCG'", 'max_length': '255', 'blank': 'True'}),
148+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
149+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': u"orm['auth.User']"})
150+ },
151+ 'webcatalog.department': {
152+ 'Meta': {'object_name': 'Department'},
153+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
155+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
156+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
157+ },
158+ 'webcatalog.distroseries': {
159+ 'Meta': {'object_name': 'DistroSeries'},
160+ 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
161+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
162+ 'prerelease': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
163+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
164+ },
165+ 'webcatalog.exhibit': {
166+ 'Meta': {'object_name': 'Exhibit'},
167+ 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
168+ 'click_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200'}),
169+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
170+ 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
171+ 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
172+ 'html': ('django.db.models.fields.TextField', [], {}),
173+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
174+ 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
175+ 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
176+ 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
177+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
178+ },
179+ 'webcatalog.machine': {
180+ 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
181+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
182+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183+ 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
184+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
185+ 'package_list': ('django.db.models.fields.TextField', [], {}),
186+ 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
187+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
188+ },
189+ 'webcatalog.nonce': {
190+ 'Meta': {'unique_together': "(('nonce', 'token', 'consumer'),)", 'object_name': 'Nonce'},
191+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
192+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
193+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
194+ 'nonce': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
195+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
196+ },
197+ 'webcatalog.reviewstatsimport': {
198+ 'Meta': {'object_name': 'ReviewStatsImport'},
199+ 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
200+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
201+ 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
202+ },
203+ 'webcatalog.token': {
204+ 'Meta': {'object_name': 'Token'},
205+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
206+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
207+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
208+ 'token': ('django.db.models.fields.CharField', [], {'default': "'KuCuRllKERFEWvVDGwtGZVBScLCUayQJucTDuMnjJopJEmcPie'", 'max_length': '50', 'primary_key': 'True'}),
209+ 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'odbGcySxXFGCRbSnhOIoicNWCkMTUGPUcyVbRDkDPptJrNRHba'", 'max_length': '50'}),
210+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
211+ }
212+ }
213+
214+ complete_apps = ['webcatalog']
215\ No newline at end of file
216
217=== modified file 'src/webcatalog/models/applications.py'
218--- src/webcatalog/models/applications.py 2013-04-23 10:18:41 +0000
219+++ src/webcatalog/models/applications.py 2014-01-10 09:19:06 +0000
220@@ -104,7 +104,7 @@
221 ratings_histogram = models.CharField(max_length=128, blank=True)
222 is_latest = models.BooleanField()
223 wilson_score = models.FloatField(null=True, blank=True, db_index=True)
224- debtags = models.CharField(max_length=255, blank=True)
225+ debtags = models.TextField(blank=True)
226 license = models.CharField(
227 max_length=64, blank=True,
228 help_text=u"The name of the license used for the app.")
229
230=== modified file 'src/webcatalog/tests/test_commands.py'
231--- src/webcatalog/tests/test_commands.py 2013-04-10 07:57:06 +0000
232+++ src/webcatalog/tests/test_commands.py 2014-01-10 09:19:06 +0000
233@@ -57,6 +57,7 @@
234 from webcatalog.management.commands import (
235 import_app_install_data,
236 import_ratings_stats,
237+ import_sca_apps,
238 )
239 from webcatalog.tests.factory import (
240 TestCaseWithFactory,
241@@ -731,6 +732,14 @@
242 self.assertEqual('natty',
243 remaining_hello_apps[0].distroseries.code_name)
244
245+ def test_import_app_from_data_returns_none(self):
246+ command = import_sca_apps.Command()
247+ precise = self.factory.make_distroseries(code_name='precise')
248+
249+ result = command.import_app_from_data({}, {}, precise)
250+
251+ self.assertIsNone(result)
252+
253
254 class ImportRatingsTestCase(TestCaseWithFactory):
255

Subscribers

People subscribed via source and target branches