Merge lp:~michael.nelson/ubuntu-webcatalog/1015515-dont-create-new-app into lp:ubuntu-webcatalog
- 1015515-dont-create-new-app
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Anthony Lenton | ||||
Approved revision: | 168 | ||||
Merged at revision: | 152 | ||||
Proposed branch: | lp:~michael.nelson/ubuntu-webcatalog/1015515-dont-create-new-app | ||||
Merge into: | lp:ubuntu-webcatalog | ||||
Prerequisite: | lp:~michael.nelson/ubuntu-webcatalog/1015505-extras-not-propietary | ||||
Diff against target: |
443 lines (+204/-70) 9 files modified
src/webcatalog/admin.py (+2/-2) src/webcatalog/forms.py (+5/-15) src/webcatalog/migrations/0024_remove_application_id_and_rename_for_purchase.py (+169/-0) src/webcatalog/models/applications.py (+1/-2) src/webcatalog/tests/factory.py (+3/-3) src/webcatalog/tests/test_commands.py (+15/-17) src/webcatalog/tests/test_forms.py (+1/-23) src/webcatalog/tests/test_managers.py (+3/-3) src/webcatalog/tests/test_views.py (+5/-5) |
||||
To merge this branch: | bzr merge lp:~michael.nelson/ubuntu-webcatalog/1015515-dont-create-new-app | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Anthony Lenton (community) | Approve | ||
Review via email: mp+112131@code.launchpad.net |
Commit message
Don't create new apps for the same distroseries and package_name.
Description of the change
Overview
========
Updates the form used to process sca app data so that we don't create new applications for the same distroseries just because they have different archive_ids.
Instead, if there's already an app in the same distroseries with the same package name, we update it.
I also fixed test_check_latest which was a false positive (it was passing if you ran all tests, but failing if I just ran the test case, because it was dependent on the alpha-ordering of the implicit versions being created by the test).
`fab test`
Notes: I'm currently left a little confused as to why we added application_id. We (I) added it with r116 as a way for us to distinguish apps imported from sca, given that we now had sca apps with archive_id=None. But this doesn't help distinguishing existing apps in the archive.
I don't know why we didn't go with package_name, or is package_name an issue because we may well *want* to have different versions of an app available in myapps. In which case, application_id still doesn't help, but checking for package_
Should we add some unique constraints for this? (unique_
RESOLVED after chatting with achuni:
* Both application_id and for_purchase allow us to distinguish between sca_imported apps and archive imported (so we can remove one of them - I've deleted application_id and renamed for_purchase-
* We only ever want one package_name per distroseries, always preferring the sca imported data. Therefore we should add a unique_together on distroseries/
- 168. By Michael Nelson
-
Remove unnecessary exclude from admin form.
Preview Diff
1 | === modified file 'src/webcatalog/admin.py' | |||
2 | --- src/webcatalog/admin.py 2012-06-06 17:42:04 +0000 | |||
3 | +++ src/webcatalog/admin.py 2012-06-28 13:32:29 +0000 | |||
4 | @@ -42,8 +42,8 @@ | |||
5 | 42 | list_display = ('package_name', 'name', 'comment', 'distroseries', | 42 | list_display = ('package_name', 'name', 'comment', 'distroseries', |
6 | 43 | 'wilson_score') | 43 | 'wilson_score') |
7 | 44 | search_fields = ('package_name', 'name', 'comment') | 44 | search_fields = ('package_name', 'name', 'comment') |
10 | 45 | list_filter = ('distroseries', 'for_purchase', 'is_latest', 'departments') | 45 | list_filter = ('distroseries', 'imported_from_sca', 'is_latest', |
11 | 46 | exclude = ('for_purchase', 'archive_id') | 46 | 'departments') |
12 | 47 | 47 | ||
13 | 48 | 48 | ||
14 | 49 | class MachineAdmin(admin.ModelAdmin): | 49 | class MachineAdmin(admin.ModelAdmin): |
15 | 50 | 50 | ||
16 | === modified file 'src/webcatalog/forms.py' | |||
17 | --- src/webcatalog/forms.py 2012-06-20 07:48:28 +0000 | |||
18 | +++ src/webcatalog/forms.py 2012-06-28 13:32:29 +0000 | |||
19 | @@ -62,7 +62,7 @@ | |||
20 | 62 | 62 | ||
21 | 63 | class Meta: | 63 | class Meta: |
22 | 64 | model = Application | 64 | model = Application |
24 | 65 | exclude = ('for_purchase', 'archive_id', 'price', 'version') | 65 | exclude = ('imported_from_sca', 'archive_id', 'price', 'version') |
25 | 66 | 66 | ||
26 | 67 | @classmethod | 67 | @classmethod |
27 | 68 | def get_form_from_desktop_data(cls, str_data, distroseries): | 68 | def get_form_from_desktop_data(cls, str_data, distroseries): |
28 | @@ -142,7 +142,7 @@ | |||
29 | 142 | def from_api_data(cls, app_data, distroseries): | 142 | def from_api_data(cls, app_data, distroseries): |
30 | 143 | app_data = app_data.copy() | 143 | app_data = app_data.copy() |
31 | 144 | app_data['distroseries'] = distroseries.id | 144 | app_data['distroseries'] = distroseries.id |
33 | 145 | app_data['for_purchase'] = True | 145 | app_data['imported_from_sca'] = True |
34 | 146 | if 'description' in app_data: | 146 | if 'description' in app_data: |
35 | 147 | tagline, _, description = app_data['description'].partition('\n') | 147 | tagline, _, description = app_data['description'].partition('\n') |
36 | 148 | app_data['comment'] = tagline | 148 | app_data['comment'] = tagline |
37 | @@ -150,21 +150,11 @@ | |||
38 | 150 | if 'debtags' in app_data and app_data['debtags']: | 150 | if 'debtags' in app_data and app_data['debtags']: |
39 | 151 | app_data['debtags'] = json.dumps([get_hw_short_description(x) | 151 | app_data['debtags'] = json.dumps([get_hw_short_description(x) |
40 | 152 | for x in app_data['debtags']]) | 152 | for x in app_data['debtags']]) |
41 | 153 | app_data['application_id'] = app_data.get('id') | ||
42 | 154 | 153 | ||
43 | 155 | try: | 154 | try: |
56 | 156 | # XXX 2012-05-03 michaeln bug=993813 We can update to use | 155 | instance = Application.objects.get( |
57 | 157 | # application_id only below once an import runs on production | 156 | package_name=app_data['package_name'], |
58 | 158 | # setting the application_ids. | 157 | distroseries=distroseries) |
47 | 159 | archive_id = app_data.get('archive_id') | ||
48 | 160 | if archive_id: | ||
49 | 161 | instance = Application.objects.get( | ||
50 | 162 | archive_id=archive_id, | ||
51 | 163 | distroseries=distroseries) | ||
52 | 164 | else: | ||
53 | 165 | instance = Application.objects.get( | ||
54 | 166 | application_id=app_data['application_id'], | ||
55 | 167 | distroseries=distroseries) | ||
59 | 168 | except Application.DoesNotExist: | 158 | except Application.DoesNotExist: |
60 | 169 | instance = None | 159 | instance = None |
61 | 170 | 160 | ||
62 | 171 | 161 | ||
63 | === added file 'src/webcatalog/migrations/0024_remove_application_id_and_rename_for_purchase.py' | |||
64 | --- src/webcatalog/migrations/0024_remove_application_id_and_rename_for_purchase.py 1970-01-01 00:00:00 +0000 | |||
65 | +++ src/webcatalog/migrations/0024_remove_application_id_and_rename_for_purchase.py 2012-06-28 13:32:29 +0000 | |||
66 | @@ -0,0 +1,169 @@ | |||
67 | 1 | # encoding: utf-8 | ||
68 | 2 | from south.db import db | ||
69 | 3 | from south.v2 import SchemaMigration | ||
70 | 4 | |||
71 | 5 | class Migration(SchemaMigration): | ||
72 | 6 | |||
73 | 7 | def forwards(self, orm): | ||
74 | 8 | # Deleting field 'Application.application_id' | ||
75 | 9 | db.delete_column('webcatalog_application', 'application_id') | ||
76 | 10 | |||
77 | 11 | # Renaming field 'Application.for_purchase' | ||
78 | 12 | db.rename_column('webcatalog_application', 'for_purchase', 'imported_from_sca') | ||
79 | 13 | |||
80 | 14 | def backwards(self, orm): | ||
81 | 15 | # Adding field 'Application.application_id' | ||
82 | 16 | db.add_column('webcatalog_application', 'application_id', | ||
83 | 17 | self.gf('django.db.models.fields.IntegerField')( | ||
84 | 18 | null=True, blank=True), keep_default=False) | ||
85 | 19 | |||
86 | 20 | # Renaming field 'Application.for_purchase' | ||
87 | 21 | db.rename_column('webcatalog_application', 'imported_from_sca', 'for_purchase') | ||
88 | 22 | |||
89 | 23 | models = { | ||
90 | 24 | 'auth.group': { | ||
91 | 25 | 'Meta': {'object_name': 'Group'}, | ||
92 | 26 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
93 | 27 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
94 | 28 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
95 | 29 | }, | ||
96 | 30 | 'auth.permission': { | ||
97 | 31 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, | ||
98 | 32 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
99 | 33 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), | ||
100 | 34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
101 | 35 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
102 | 36 | }, | ||
103 | 37 | 'auth.user': { | ||
104 | 38 | 'Meta': {'object_name': 'User'}, | ||
105 | 39 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
106 | 40 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), | ||
107 | 41 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
108 | 42 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), | ||
109 | 43 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
110 | 44 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
111 | 45 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
112 | 46 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
113 | 47 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
114 | 48 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
115 | 49 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
116 | 50 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), | ||
117 | 51 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) | ||
118 | 52 | }, | ||
119 | 53 | 'contenttypes.contenttype': { | ||
120 | 54 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
121 | 55 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
122 | 56 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
123 | 57 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
124 | 58 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
125 | 59 | }, | ||
126 | 60 | 'webcatalog.application': { | ||
127 | 61 | 'Meta': {'ordering': "('-wilson_score', 'name')", 'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'}, | ||
128 | 62 | 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), | ||
129 | 63 | 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
130 | 64 | 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), | ||
131 | 65 | 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
132 | 66 | 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
133 | 67 | 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
134 | 68 | 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
135 | 69 | 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}), | ||
136 | 70 | 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
137 | 71 | 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}), | ||
138 | 72 | 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
139 | 73 | 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
140 | 74 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
141 | 75 | 'imported_from_sca': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
142 | 76 | 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
143 | 77 | 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
144 | 78 | 'license': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), | ||
145 | 79 | 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}), | ||
146 | 80 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
147 | 81 | 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
148 | 82 | 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
149 | 83 | 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}), | ||
150 | 84 | 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}), | ||
151 | 85 | 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), | ||
152 | 86 | 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
153 | 87 | 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), | ||
154 | 88 | 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), | ||
155 | 89 | 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}) | ||
156 | 90 | }, | ||
157 | 91 | 'webcatalog.applicationmedia': { | ||
158 | 92 | 'Meta': {'ordering': "('url',)", 'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'}, | ||
159 | 93 | 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}), | ||
160 | 94 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
161 | 95 | 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), | ||
162 | 96 | 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}) | ||
163 | 97 | }, | ||
164 | 98 | 'webcatalog.consumer': { | ||
165 | 99 | 'Meta': {'object_name': 'Consumer'}, | ||
166 | 100 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
167 | 101 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
168 | 102 | 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
169 | 103 | 'secret': ('django.db.models.fields.CharField', [], {'default': "'hyPFlJHgDizTUdnTNWGRLtVITxkCXv'", 'max_length': '255', 'blank': 'True'}), | ||
170 | 104 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), | ||
171 | 105 | 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) | ||
172 | 106 | }, | ||
173 | 107 | 'webcatalog.department': { | ||
174 | 108 | 'Meta': {'object_name': 'Department'}, | ||
175 | 109 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
176 | 110 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
177 | 111 | 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}), | ||
178 | 112 | 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) | ||
179 | 113 | }, | ||
180 | 114 | 'webcatalog.distroseries': { | ||
181 | 115 | 'Meta': {'object_name': 'DistroSeries'}, | ||
182 | 116 | 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), | ||
183 | 117 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
184 | 118 | 'prerelease': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
185 | 119 | 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) | ||
186 | 120 | }, | ||
187 | 121 | 'webcatalog.exhibit': { | ||
188 | 122 | 'Meta': {'object_name': 'Exhibit'}, | ||
189 | 123 | 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), | ||
190 | 124 | 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
191 | 125 | 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), | ||
192 | 126 | 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}), | ||
193 | 127 | 'html': ('django.db.models.fields.TextField', [], {}), | ||
194 | 128 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
195 | 129 | 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), | ||
196 | 130 | 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
197 | 131 | 'sca_id': ('django.db.models.fields.IntegerField', [], {}), | ||
198 | 132 | 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'}) | ||
199 | 133 | }, | ||
200 | 134 | 'webcatalog.machine': { | ||
201 | 135 | 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'}, | ||
202 | 136 | 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
203 | 137 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
204 | 138 | 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}), | ||
205 | 139 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), | ||
206 | 140 | 'package_list': ('django.db.models.fields.TextField', [], {}), | ||
207 | 141 | 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}), | ||
208 | 142 | 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}) | ||
209 | 143 | }, | ||
210 | 144 | 'webcatalog.nonce': { | ||
211 | 145 | 'Meta': {'object_name': 'Nonce'}, | ||
212 | 146 | 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), | ||
213 | 147 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
214 | 148 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
215 | 149 | 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
216 | 150 | 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"}) | ||
217 | 151 | }, | ||
218 | 152 | 'webcatalog.reviewstatsimport': { | ||
219 | 153 | 'Meta': {'object_name': 'ReviewStatsImport'}, | ||
220 | 154 | 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}), | ||
221 | 155 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
222 | 156 | 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) | ||
223 | 157 | }, | ||
224 | 158 | 'webcatalog.token': { | ||
225 | 159 | 'Meta': {'object_name': 'Token'}, | ||
226 | 160 | 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), | ||
227 | 161 | 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
228 | 162 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), | ||
229 | 163 | 'token': ('django.db.models.fields.CharField', [], {'default': "'jfRUKQyKMbFDEhdHVnneCWnhZtSCSALtPMdkMsVgDZgpYnjpme'", 'max_length': '50', 'primary_key': 'True'}), | ||
230 | 164 | 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'lwXqDRKNlctQNbzZSQkHyKccayMHZlFtpbPngazmvxMKywrNKS'", 'max_length': '50'}), | ||
231 | 165 | 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) | ||
232 | 166 | } | ||
233 | 167 | } | ||
234 | 168 | |||
235 | 169 | complete_apps = ['webcatalog'] | ||
236 | 0 | 170 | ||
237 | === modified file 'src/webcatalog/models/applications.py' | |||
238 | --- src/webcatalog/models/applications.py 2012-06-26 09:08:44 +0000 | |||
239 | +++ src/webcatalog/models/applications.py 2012-06-28 13:32:29 +0000 | |||
240 | @@ -87,13 +87,12 @@ | |||
241 | 87 | icon_name = models.CharField(max_length=255, blank=True) | 87 | icon_name = models.CharField(max_length=255, blank=True) |
242 | 88 | icon = models.ImageField( | 88 | icon = models.ImageField( |
243 | 89 | upload_to='icons/%Y/%m', max_length=200, null=True, blank=True) | 89 | upload_to='icons/%Y/%m', max_length=200, null=True, blank=True) |
245 | 90 | for_purchase = models.BooleanField(default=False) | 90 | imported_from_sca = models.BooleanField(default=False) |
246 | 91 | archive_id = models.CharField( | 91 | archive_id = models.CharField( |
247 | 92 | max_length=64, null=True, db_index=True, blank=True) | 92 | max_length=64, null=True, db_index=True, blank=True) |
248 | 93 | price = models.DecimalField( | 93 | price = models.DecimalField( |
249 | 94 | max_digits=7, decimal_places=2, null=True, | 94 | max_digits=7, decimal_places=2, null=True, |
250 | 95 | blank=True, help_text="For-purchase applications (in US Dollars).") | 95 | blank=True, help_text="For-purchase applications (in US Dollars).") |
251 | 96 | application_id = models.IntegerField(null=True, blank=True) | ||
252 | 97 | 96 | ||
253 | 98 | ratings_total = models.IntegerField(null=True, blank=True) | 97 | ratings_total = models.IntegerField(null=True, blank=True) |
254 | 99 | ratings_average = models.DecimalField( | 98 | ratings_average = models.DecimalField( |
255 | 100 | 99 | ||
256 | === modified file 'src/webcatalog/tests/factory.py' | |||
257 | --- src/webcatalog/tests/factory.py 2012-06-26 09:18:42 +0000 | |||
258 | +++ src/webcatalog/tests/factory.py 2012-06-28 13:32:29 +0000 | |||
259 | @@ -103,8 +103,8 @@ | |||
260 | 103 | ratings_average=None, ratings_total=None, | 103 | ratings_average=None, ratings_total=None, |
261 | 104 | ratings_histogram='', screenshot_url='', | 104 | ratings_histogram='', screenshot_url='', |
262 | 105 | archive_id=None, version='', is_latest=False, | 105 | archive_id=None, version='', is_latest=False, |
265 | 106 | wilson_score=0.0, debtags=[], application_id=None, | 106 | wilson_score=0.0, debtags=[], departments=None, |
266 | 107 | departments=None, license='', price=None): | 107 | license='', price=None): |
267 | 108 | if name is None: | 108 | if name is None: |
268 | 109 | name = self.get_unique_string(prefix='Readable Name') | 109 | name = self.get_unique_string(prefix='Readable Name') |
269 | 110 | if package_name is None: | 110 | if package_name is None: |
270 | @@ -124,7 +124,7 @@ | |||
271 | 124 | ratings_histogram=ratings_histogram, | 124 | ratings_histogram=ratings_histogram, |
272 | 125 | archive_id=archive_id, version=version, is_latest=is_latest, | 125 | archive_id=archive_id, version=version, is_latest=is_latest, |
273 | 126 | wilson_score=wilson_score, debtags=debtags, | 126 | wilson_score=wilson_score, debtags=debtags, |
275 | 127 | application_id=application_id, license=license, price=price) | 127 | license=license, price=price) |
276 | 128 | 128 | ||
277 | 129 | if departments is not None: | 129 | if departments is not None: |
278 | 130 | for d in departments: | 130 | for d in departments: |
279 | 131 | 131 | ||
280 | === modified file 'src/webcatalog/tests/test_commands.py' | |||
281 | --- src/webcatalog/tests/test_commands.py 2012-06-26 09:00:21 +0000 | |||
282 | +++ src/webcatalog/tests/test_commands.py 2012-06-28 13:32:29 +0000 | |||
283 | @@ -577,17 +577,18 @@ | |||
284 | 577 | self.mock_urlopen.side_effect = mock_urlopen_fn | 577 | self.mock_urlopen.side_effect = mock_urlopen_fn |
285 | 578 | self.addCleanup(self.patch_urlopen.stop) | 578 | self.addCleanup(self.patch_urlopen.stop) |
286 | 579 | 579 | ||
290 | 580 | def test_app_for_purchase_is_found(self): | 580 | def test_app_imported_from_sca_is_found(self): |
291 | 581 | apps_for_purchase = Application.objects.filter(for_purchase=True) | 581 | apps_imported_from_sca = Application.objects.filter( |
292 | 582 | self.assertEqual(0, len(apps_for_purchase)) | 582 | imported_from_sca=True) |
293 | 583 | self.assertEqual(0, len(apps_imported_from_sca)) | ||
294 | 583 | 584 | ||
295 | 584 | call_command('import_for_purchase_apps') | 585 | call_command('import_for_purchase_apps') |
296 | 585 | 586 | ||
302 | 586 | app_for_purchase = Application.objects.get(name='MyApp', | 587 | app = Application.objects.get(name='MyApp', |
303 | 587 | distroseries=self.natty) | 588 | distroseries=self.natty) |
304 | 588 | self.assertEqual(True, app_for_purchase.for_purchase) | 589 | self.assertEqual(True, app.imported_from_sca) |
305 | 589 | self.assertTrue(app_for_purchase.description.find('hello') > -1) | 590 | self.assertTrue(app.description.find('hello') > -1) |
306 | 590 | self.assertEqual("Proprietary", app_for_purchase.license) | 591 | self.assertEqual("Proprietary", app.license) |
307 | 591 | 592 | ||
308 | 592 | def test_app_gets_distroseries(self): | 593 | def test_app_gets_distroseries(self): |
309 | 593 | with patch_settings(UBUNTU_SERIES_FOR_VERSIONS=TEST_VERSIONS): | 594 | with patch_settings(UBUNTU_SERIES_FOR_VERSIONS=TEST_VERSIONS): |
310 | @@ -613,13 +614,11 @@ | |||
311 | 613 | distroseries=self.natty) | 614 | distroseries=self.natty) |
312 | 614 | self.assertEqual(Decimal('2.50'), app.price) | 615 | self.assertEqual(Decimal('2.50'), app.price) |
313 | 615 | 616 | ||
321 | 616 | def test_existing_app_gets_updated_by_archive_id(self): | 617 | def test_existing_app_gets_updated_by_package_name(self): |
322 | 617 | self.factory.make_application( | 618 | self.factory.make_application( |
323 | 618 | archive_id='launchpad_zematynnad2/myppa', | 619 | package_name='hello', distroseries=self.maverick) |
324 | 619 | package_name='somethingelse', distroseries=self.maverick) | 620 | self.factory.make_application( |
325 | 620 | self.factory.make_application( | 621 | package_name='hello', distroseries=self.natty) |
319 | 621 | archive_id='launchpad_zematynnad2/myppa', | ||
320 | 622 | package_name='somethingelse', distroseries=self.natty) | ||
326 | 623 | apps = Application.objects.filter( | 622 | apps = Application.objects.filter( |
327 | 624 | distroseries__in=(self.natty, self.maverick)).count() | 623 | distroseries__in=(self.natty, self.maverick)).count() |
328 | 625 | self.assertEqual(2, apps) | 624 | self.assertEqual(2, apps) |
329 | @@ -631,8 +630,7 @@ | |||
330 | 631 | distroseries__in=(self.natty, self.maverick)) | 630 | distroseries__in=(self.natty, self.maverick)) |
331 | 632 | self.assertEqual(2, actual_apps.count()) | 631 | self.assertEqual(2, actual_apps.count()) |
332 | 633 | for app in actual_apps: | 632 | for app in actual_apps: |
335 | 634 | self.assertEqual('launchpad_zematynnad2/myppa', app.archive_id) | 633 | self.assertTrue(app.imported_from_sca) |
334 | 635 | self.assertEqual('hello', app.package_name) | ||
336 | 636 | 634 | ||
337 | 637 | def test_app_gets_icon(self): | 635 | def test_app_gets_icon(self): |
338 | 638 | call_command('import_for_purchase_apps') | 636 | call_command('import_for_purchase_apps') |
339 | 639 | 637 | ||
340 | === modified file 'src/webcatalog/tests/test_forms.py' | |||
341 | --- src/webcatalog/tests/test_forms.py 2012-06-13 08:53:46 +0000 | |||
342 | +++ src/webcatalog/tests/test_forms.py 2012-06-28 13:32:29 +0000 | |||
343 | @@ -176,7 +176,7 @@ | |||
344 | 176 | 176 | ||
345 | 177 | def test_from_api_data_shortens_debtag(self): | 177 | def test_from_api_data_shortens_debtag(self): |
346 | 178 | app = self.factory.make_application() | 178 | app = self.factory.make_application() |
348 | 179 | data = {'debtags': ['hardware::storage:cd-writer']} | 179 | data = self.make_valid_data(debtags=['hardware::storage:cd-writer']) |
349 | 180 | 180 | ||
350 | 181 | form = ForPurchaseApplicationForm.from_api_data(data, app.distroseries) | 181 | form = ForPurchaseApplicationForm.from_api_data(data, app.distroseries) |
351 | 182 | 182 | ||
352 | @@ -252,28 +252,6 @@ | |||
353 | 252 | data.update(**kwargs) | 252 | data.update(**kwargs) |
354 | 253 | return data | 253 | return data |
355 | 254 | 254 | ||
356 | 255 | def test_archive_id_none_uses_application_id(self): | ||
357 | 256 | precise = self.factory.make_distroseries(code_name='precise') | ||
358 | 257 | existing_app = self.factory.make_application( | ||
359 | 258 | archive_id=None, application_id=21, distroseries=precise) | ||
360 | 259 | |||
361 | 260 | data = self.make_valid_data(archive_id=None, id=21) | ||
362 | 261 | form = ForPurchaseApplicationForm.from_api_data(data, precise) | ||
363 | 262 | |||
364 | 263 | self.assertEqual(existing_app, form.instance) | ||
365 | 264 | |||
366 | 265 | def test_updates_application_id(self): | ||
367 | 266 | precise = self.factory.make_distroseries(code_name='precise') | ||
368 | 267 | existing_app = self.factory.make_application( | ||
369 | 268 | archive_id='foo/bar', application_id=None, distroseries=precise) | ||
370 | 269 | |||
371 | 270 | data = self.make_valid_data(archive_id='foo/bar', id=21) | ||
372 | 271 | form = ForPurchaseApplicationForm.from_api_data(data, precise) | ||
373 | 272 | form.save() | ||
374 | 273 | |||
375 | 274 | app_reloaded = Application.objects.get(id=existing_app.id) | ||
376 | 275 | self.assertEqual(21, app_reloaded.application_id) | ||
377 | 276 | |||
378 | 277 | def test_archive_id_is_set_to_none_when_supplied_as_empty_str(self): | 255 | def test_archive_id_is_set_to_none_when_supplied_as_empty_str(self): |
379 | 278 | """Check that bug 1010655 is not happening again.""" | 256 | """Check that bug 1010655 is not happening again.""" |
380 | 279 | precise = self.factory.make_distroseries(code_name='precise') | 257 | precise = self.factory.make_distroseries(code_name='precise') |
381 | 280 | 258 | ||
382 | === modified file 'src/webcatalog/tests/test_managers.py' | |||
383 | --- src/webcatalog/tests/test_managers.py 2012-06-06 18:00:26 +0000 | |||
384 | +++ src/webcatalog/tests/test_managers.py 2012-06-28 13:32:29 +0000 | |||
385 | @@ -50,14 +50,14 @@ | |||
386 | 50 | self.assertEqual(expected.id, retrieved.id) | 50 | self.assertEqual(expected.id, retrieved.id) |
387 | 51 | 51 | ||
388 | 52 | def test_check_latest(self): | 52 | def test_check_latest(self): |
391 | 53 | for code_name in ['lucid', 'maverick', 'natty', 'oneiric']: | 53 | for version in ['10.04', '10.10', '11.04', '11.10']: |
392 | 54 | dseries = self.factory.make_distroseries(code_name=code_name) | 54 | dseries = self.factory.make_distroseries(version=version) |
393 | 55 | self.factory.make_application( | 55 | self.factory.make_application( |
394 | 56 | package_name='foobar', distroseries=dseries) | 56 | package_name='foobar', distroseries=dseries) |
395 | 57 | 57 | ||
396 | 58 | Application.objects.check_latest('foobar') | 58 | Application.objects.check_latest('foobar') |
397 | 59 | 59 | ||
398 | 60 | retrieved = Application.objects.filter(package_name='foobar').order_by( | 60 | retrieved = Application.objects.filter(package_name='foobar').order_by( |
400 | 61 | '-distroseries__code_name') | 61 | '-distroseries__version') |
401 | 62 | self.assertEqual([True, False, False, False], | 62 | self.assertEqual([True, False, False, False], |
402 | 63 | [app.is_latest for app in retrieved]) | 63 | [app.is_latest for app in retrieved]) |
403 | 64 | 64 | ||
404 | === modified file 'src/webcatalog/tests/test_views.py' | |||
405 | --- src/webcatalog/tests/test_views.py 2012-06-26 09:40:37 +0000 | |||
406 | +++ src/webcatalog/tests/test_views.py 2012-06-28 13:32:29 +0000 | |||
407 | @@ -91,7 +91,7 @@ | |||
408 | 91 | def get_app_and_response(self, code_name='natty', version='11.04', | 91 | def get_app_and_response(self, code_name='natty', version='11.04', |
409 | 92 | arch='x86_64', name=None, comment=None, | 92 | arch='x86_64', name=None, comment=None, |
410 | 93 | description=None, detail_distro=None, | 93 | description=None, detail_distro=None, |
412 | 94 | detail_package=None, for_purchase=False, | 94 | detail_package=None, imported_from_sca=False, |
413 | 95 | screenshot_url='', useragent=UBUNTU_USERAGENT): | 95 | screenshot_url='', useragent=UBUNTU_USERAGENT): |
414 | 96 | series, created = DistroSeries.objects.get_or_create( | 96 | series, created = DistroSeries.objects.get_or_create( |
415 | 97 | code_name=code_name, version=version) | 97 | code_name=code_name, version=version) |
416 | @@ -100,8 +100,8 @@ | |||
417 | 100 | name=name, comment=comment, | 100 | name=name, comment=comment, |
418 | 101 | description=description, | 101 | description=description, |
419 | 102 | screenshot_url=screenshot_url) | 102 | screenshot_url=screenshot_url) |
422 | 103 | if for_purchase: | 103 | if imported_from_sca: |
423 | 104 | app.for_purchase = True | 104 | app.imported_from_sca = True |
424 | 105 | app.save() | 105 | app.save() |
425 | 106 | if not detail_distro: | 106 | if not detail_distro: |
426 | 107 | detail_distro = app.distroseries.code_name | 107 | detail_distro = app.distroseries.code_name |
427 | @@ -168,7 +168,7 @@ | |||
428 | 168 | 168 | ||
429 | 169 | def test_button_for_non_puchase_app(self): | 169 | def test_button_for_non_puchase_app(self): |
430 | 170 | self.factory.make_application(package_name='pkgfoo', | 170 | self.factory.make_application(package_name='pkgfoo', |
432 | 171 | price=None) | 171 | price=None) |
433 | 172 | 172 | ||
434 | 173 | response = self.get_package_details_response('pkgfoo') | 173 | response = self.get_package_details_response('pkgfoo') |
435 | 174 | 174 | ||
436 | @@ -178,7 +178,7 @@ | |||
437 | 178 | 178 | ||
438 | 179 | def test_button_for_for_puchase_app(self): | 179 | def test_button_for_for_puchase_app(self): |
439 | 180 | self.factory.make_application(package_name='pkgfoo', | 180 | self.factory.make_application(package_name='pkgfoo', |
441 | 181 | price=Decimal('12.99')) | 181 | price=Decimal('12.99')) |
442 | 182 | 182 | ||
443 | 183 | response = self.get_package_details_response('pkgfoo') | 183 | response = self.get_package_details_response('pkgfoo') |
444 | 184 | 184 |
Looks good! :)
+1 to remove application_id, and rename for_purchase -> imported_from_sca too.