Merge lp:~michael.nelson/rnr-server/dont-depend-on-appname into lp:rnr-server
- dont-depend-on-appname
- Merge into trunk
Proposed by
Michael Nelson
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Anthony Lenton | ||||
Approved revision: | 173 | ||||
Merged at revision: | 162 | ||||
Proposed branch: | lp:~michael.nelson/rnr-server/dont-depend-on-appname | ||||
Merge into: | lp:rnr-server | ||||
Diff against target: |
583 lines (+417/-20) 8 files modified
src/reviewsapp/api/handlers.py (+1/-1) src/reviewsapp/management/commands/populate.py (+2/-2) src/reviewsapp/migrations/0006_add_review_app_name.py (+160/-0) src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py (+234/-0) src/reviewsapp/models/reviews.py (+6/-4) src/reviewsapp/tests/factory.py (+3/-2) src/reviewsapp/tests/test_handlers.py (+8/-8) src/reviewsapp/tests/test_rnrclient.py (+3/-3) |
||||
To merge this branch: | bzr merge lp:~michael.nelson/rnr-server/dont-depend-on-appname | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Anthony Lenton (community) | Approve | ||
Review via email: mp+54013@code.launchpad.net |
Commit message
[r=achuni][bug=737649] Data migration from SoftwareItem.
Description of the change
Overview
========
This branch adds a schema migration (add Review.app_name column) and a data migration (copy app_name from software items, then remove now-redundant software items).
Unfortunately, testing the migrations sux. The best I could come up with is:
http://
which ensures that for a small subset of data at least, the migration works both forwards and backwards without losing info.
The next branch:
https:/
removes the actual SoftwareItem.
To post a comment you must log in.
Revision history for this message
Anthony Lenton (elachuni) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/reviewsapp/api/handlers.py' |
2 | --- src/reviewsapp/api/handlers.py 2011-03-17 14:29:54 +0000 |
3 | +++ src/reviewsapp/api/handlers.py 2011-03-18 17:09:28 +0000 |
4 | @@ -191,7 +191,7 @@ |
5 | kwargs = dict(item for item in kwargs.items() if item[1] != 'any') |
6 | return Review.objects.filter( |
7 | softwareitem__package_name=package_name, |
8 | - softwareitem__app_name=appname, |
9 | + app_name=appname, |
10 | hide=False, |
11 | **kwargs).order_by( |
12 | '-usefulness_percentage')[batch_start:batch_end] |
13 | |
14 | === modified file 'src/reviewsapp/management/commands/populate.py' |
15 | --- src/reviewsapp/management/commands/populate.py 2011-03-15 14:08:02 +0000 |
16 | +++ src/reviewsapp/management/commands/populate.py 2011-03-18 17:09:28 +0000 |
17 | @@ -182,7 +182,7 @@ |
18 | Currently (id, softwareitem_id, version, architecture_id, |
19 | repository_id, reviewer_id, date_created, rating, summary, |
20 | review_text, language, hide, usefulness_total, |
21 | - usefulness_favorable, usefulness_percentage) |
22 | + usefulness_favorable, usefulness_percentage, app_name) |
23 | """ |
24 | softwareitem_id = randint(*self.ranges['reviewsapp_softwareitem']) |
25 | version = '%.1f' % random() |
26 | @@ -195,5 +195,5 @@ |
27 | review_text = self.random_comment(wordcount=20) |
28 | false = self.booleans[0] |
29 | return [id, softwareitem_id, version, arch_id, repo_id, reviewer_id, |
30 | - date_created, rating, summary, review_text, 'en', false, 0, 0, 0] |
31 | + date_created, rating, summary, review_text, 'en', false, 0, 0, 0, ''] |
32 | |
33 | |
34 | === added file 'src/reviewsapp/migrations/0006_add_review_app_name.py' |
35 | --- src/reviewsapp/migrations/0006_add_review_app_name.py 1970-01-01 00:00:00 +0000 |
36 | +++ src/reviewsapp/migrations/0006_add_review_app_name.py 2011-03-18 17:09:28 +0000 |
37 | @@ -0,0 +1,160 @@ |
38 | + |
39 | +from south.db import db |
40 | +from django.db import models |
41 | +from reviewsapp.models import * |
42 | + |
43 | +class Migration: |
44 | + |
45 | + def forwards(self, orm): |
46 | + |
47 | + # Adding field 'Review.app_name' |
48 | + db.add_column('reviewsapp_review', 'app_name', orm['reviewsapp.review:app_name']) |
49 | + |
50 | + def backwards(self, orm): |
51 | + |
52 | + # Deleting field 'Review.app_name' |
53 | + db.delete_column('reviewsapp_review', 'app_name') |
54 | + |
55 | + |
56 | + models = { |
57 | + 'auth.group': { |
58 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
59 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
60 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
61 | + }, |
62 | + 'auth.permission': { |
63 | + 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, |
64 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
65 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
66 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
67 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
68 | + }, |
69 | + 'auth.user': { |
70 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
71 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
72 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
73 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
74 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
75 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
76 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
77 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
78 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
79 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
80 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
81 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
82 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
83 | + }, |
84 | + 'contenttypes.contenttype': { |
85 | + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, |
86 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
87 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
88 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
89 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
90 | + }, |
91 | + 'reviewsapp.architecture': { |
92 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
93 | + 'tag': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'}) |
94 | + }, |
95 | + 'reviewsapp.consumer': { |
96 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
97 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
98 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
99 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'OzuURCXRwhChWIMXbLbcgooiStFeze'", 'max_length': '255', 'blank': 'True'}), |
100 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
101 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) |
102 | + }, |
103 | + 'reviewsapp.nonce': { |
104 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}), |
105 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
106 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
107 | + 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
108 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Token']"}) |
109 | + }, |
110 | + 'reviewsapp.repository': { |
111 | + 'distroseries': ('django.db.models.fields.SlugField', [], {'max_length': '25', 'db_index': 'True'}), |
112 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
113 | + 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}) |
114 | + }, |
115 | + 'reviewsapp.review': { |
116 | + 'app_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), |
117 | + 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Architecture']"}), |
118 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 823060)'}), |
119 | + 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
120 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
121 | + 'language': ('django.db.models.fields.SlugField', [], {'max_length': '10', 'db_index': 'True'}), |
122 | + 'rating': ('django.db.models.fields.IntegerField', [], {}), |
123 | + 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}), |
124 | + 'review_text': ('django.db.models.fields.CharField', [], {'max_length': '5000'}), |
125 | + 'reviewer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
126 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}), |
127 | + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '80'}), |
128 | + 'usefulness_favorable': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
129 | + 'usefulness_percentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
130 | + 'usefulness_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
131 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
132 | + }, |
133 | + 'reviewsapp.reviewmoderation': { |
134 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 770486)'}), |
135 | + 'date_moderated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
136 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
137 | + 'moderation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
138 | + 'moderator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
139 | + 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}), |
140 | + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}) |
141 | + }, |
142 | + 'reviewsapp.reviewmoderationflag': { |
143 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 569414)'}), |
144 | + 'description': ('django.db.models.fields.TextField', [], {}), |
145 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
146 | + 'review_moderation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.ReviewModeration']"}), |
147 | + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
148 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) |
149 | + }, |
150 | + 'reviewsapp.rnrsettings': { |
151 | + 'blacklist_words': ('django.db.models.fields.TextField', [], {'default': "''"}), |
152 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
153 | + 'moderation_mode': ('django.db.models.fields.CharField', [], {'default': "'passive'", 'max_length': '8'}) |
154 | + }, |
155 | + 'reviewsapp.softwareitem': { |
156 | + 'app_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), |
157 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}), |
158 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
159 | + 'package_name': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}), |
160 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
161 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}) |
162 | + }, |
163 | + 'reviewsapp.softwareiteminorigin': { |
164 | + 'Meta': {'unique_together': "(('softwareitem', 'origin'),)"}, |
165 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}), |
166 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
167 | + 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}), |
168 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
169 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
170 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}) |
171 | + }, |
172 | + 'reviewsapp.softwareiteminrepository': { |
173 | + 'Meta': {'unique_together': "(('softwareitem', 'repository'),)"}, |
174 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}), |
175 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
176 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
177 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
178 | + 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}), |
179 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}) |
180 | + }, |
181 | + 'reviewsapp.token': { |
182 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}), |
183 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
184 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
185 | + 'token': ('django.db.models.fields.CharField', [], {'default': "'LXGJOoldWHCXXULnsTXhHOGmAaKCYUrXJRRgrfUjMaJfObdlEU'", 'max_length': '50', 'primary_key': 'True'}), |
186 | + 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'MnRkFdkPZXmIqCKNExfyzqdWifIWiWaoZGQKNdtoounoziJVsU'", 'max_length': '50'}), |
187 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
188 | + }, |
189 | + 'reviewsapp.usefulness': { |
190 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
191 | + 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}), |
192 | + 'useful': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
193 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) |
194 | + } |
195 | + } |
196 | + |
197 | + complete_apps = ['reviewsapp'] |
198 | |
199 | === added file 'src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py' |
200 | --- src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py 1970-01-01 00:00:00 +0000 |
201 | +++ src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py 2011-03-18 17:09:28 +0000 |
202 | @@ -0,0 +1,234 @@ |
203 | + |
204 | +from south.db import db |
205 | +from django.db import models |
206 | +from reviewsapp.models import * |
207 | + |
208 | +class Migration: |
209 | + |
210 | + def forwards(self, orm): |
211 | + """Copy the app name from the software item and update the link.""" |
212 | + # For each software_item, we want to update all the related |
213 | + # reviews. |
214 | + for software_item in orm['reviewsapp.softwareitem'].objects.all(): |
215 | + software_item.review_set.update( |
216 | + app_name=software_item.app_name) |
217 | + |
218 | + # Next, we ensure that all the reviews for a given packagename |
219 | + # refer to a single software_item, and delete the redundant |
220 | + # softawre_items. |
221 | + package_names = orm['reviewsapp.softwareitem'].objects.values_list( |
222 | + 'package_name', flat=True) |
223 | + package_names = package_names.order_by('package_name').distinct() |
224 | + for package_name in package_names: |
225 | + # Grab all the reviews for a given package name and update |
226 | + # them to all point to the one software item. |
227 | + reviews = orm['reviewsapp.review'].objects.filter( |
228 | + softwareitem__package_name=package_name) |
229 | + first_review = reviews[0] |
230 | + remaining_software_item = reviews[0].softwareitem |
231 | + reviews.update(softwareitem=remaining_software_item) |
232 | + |
233 | + # Now delete all the other software items with the same |
234 | + # package name (and software item in origins / |
235 | + # repositories). |
236 | + orm['reviewsapp.softwareitem'].objects.filter( |
237 | + package_name=package_name).exclude( |
238 | + id=remaining_software_item.id).delete() |
239 | + |
240 | + # Finally, we need to ensure that each review corresponds |
241 | + # to a valid SoftwareItemInRepository so that stats can be |
242 | + # calculated. |
243 | + self.ensure_softwareitem_in_repos(orm, remaining_software_item) |
244 | + |
245 | + def backwards(self, orm): |
246 | + """Create the software items with app names and update reviews.""" |
247 | + SoftwareItemClass = orm['reviewsapp.softwareitem'] |
248 | + for software_item in SoftwareItemClass.objects.all(): |
249 | + app_names = software_item.review_set.values_list( |
250 | + 'app_name', flat=True).distinct() |
251 | + software_item.app_name = app_names[0] |
252 | + software_item.save() |
253 | + package_name = software_item.package_name |
254 | + # We need to recreate the repository/origin stats for |
255 | + # the existing softwareitem. |
256 | + software_item.softwareiteminrepository_set.all().delete() |
257 | + software_item.softwareiteminorigin_set.all().delete() |
258 | + |
259 | + # For any other app names, we create a new software item and |
260 | + # point the relevant reviews to it. |
261 | + for app_name in app_names: |
262 | + new_item, created = SoftwareItemClass.objects.get_or_create( |
263 | + package_name=package_name, |
264 | + app_name=app_name) |
265 | + |
266 | + orm['reviewsapp.review'].objects.filter( |
267 | + softwareitem=software_item, |
268 | + app_name=app_name).update( |
269 | + softwareitem=new_item) |
270 | + |
271 | + # Once all review.software_items have been updated, we can |
272 | + # create the related items in repos etc. |
273 | + for item in SoftwareItemClass.objects.all(): |
274 | + self.ensure_softwareitem_in_repos(orm, item) |
275 | + |
276 | + # Finally, blank out the Review.app_name field. |
277 | + orm['reviewsapp.review'].objects.all().update(app_name='') |
278 | + |
279 | + def ensure_softwareitem_in_repos(self, orm, software_item): |
280 | + # Find all reviews referencing the item and get a list of |
281 | + # distint repositories. |
282 | + reviews = orm['reviewsapp.review'].objects.filter( |
283 | + softwareitem=software_item) |
284 | + repository_ids = reviews.values_list('repository', flat=True).distinct() |
285 | + SIIRepo = orm['reviewsapp.softwareiteminrepository'] |
286 | + SIIOrigin = orm['reviewsapp.softwareiteminorigin'] |
287 | + for repo_id in repository_ids: |
288 | + repo = orm['reviewsapp.repository'].objects.get(id=repo_id) |
289 | + item_in_repo, created = SIIRepo.objects.get_or_create( |
290 | + softwareitem=software_item, repository=repo) |
291 | + item_in_origin, created = SIIOrigin.objects.get_or_create( |
292 | + softwareitem=software_item, origin=repo.origin) |
293 | + |
294 | + |
295 | + models = { |
296 | + 'auth.group': { |
297 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
298 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
299 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
300 | + }, |
301 | + 'auth.permission': { |
302 | + 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, |
303 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
304 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
305 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
306 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
307 | + }, |
308 | + 'auth.user': { |
309 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
310 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
311 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
312 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
313 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
314 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
315 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
316 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
317 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
318 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
319 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
320 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
321 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
322 | + }, |
323 | + 'contenttypes.contenttype': { |
324 | + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, |
325 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
326 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
327 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
328 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
329 | + }, |
330 | + 'reviewsapp.architecture': { |
331 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
332 | + 'tag': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'}) |
333 | + }, |
334 | + 'reviewsapp.consumer': { |
335 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
336 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
337 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
338 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'qBxqBnulneXJvOyccXpAJdJmhzatIw'", 'max_length': '255', 'blank': 'True'}), |
339 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
340 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) |
341 | + }, |
342 | + 'reviewsapp.nonce': { |
343 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}), |
344 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
345 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
346 | + 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
347 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Token']"}) |
348 | + }, |
349 | + 'reviewsapp.repository': { |
350 | + 'distroseries': ('django.db.models.fields.SlugField', [], {'max_length': '25', 'db_index': 'True'}), |
351 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
352 | + 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}) |
353 | + }, |
354 | + 'reviewsapp.review': { |
355 | + 'app_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), |
356 | + 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Architecture']"}), |
357 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 823749)'}), |
358 | + 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
359 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
360 | + 'language': ('django.db.models.fields.SlugField', [], {'max_length': '10', 'db_index': 'True'}), |
361 | + 'rating': ('django.db.models.fields.IntegerField', [], {}), |
362 | + 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}), |
363 | + 'review_text': ('django.db.models.fields.CharField', [], {'max_length': '5000'}), |
364 | + 'reviewer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
365 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}), |
366 | + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '80'}), |
367 | + 'usefulness_favorable': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
368 | + 'usefulness_percentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
369 | + 'usefulness_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
370 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
371 | + }, |
372 | + 'reviewsapp.reviewmoderation': { |
373 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 959691)'}), |
374 | + 'date_moderated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
375 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
376 | + 'moderation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
377 | + 'moderator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
378 | + 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}), |
379 | + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}) |
380 | + }, |
381 | + 'reviewsapp.reviewmoderationflag': { |
382 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 12, 530560)'}), |
383 | + 'description': ('django.db.models.fields.TextField', [], {}), |
384 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
385 | + 'review_moderation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.ReviewModeration']"}), |
386 | + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
387 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) |
388 | + }, |
389 | + 'reviewsapp.rnrsettings': { |
390 | + 'blacklist_words': ('django.db.models.fields.TextField', [], {'default': "''"}), |
391 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
392 | + 'moderation_mode': ('django.db.models.fields.CharField', [], {'default': "'passive'", 'max_length': '8'}) |
393 | + }, |
394 | + 'reviewsapp.softwareitem': { |
395 | + 'app_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True', 'default': "''"}), |
396 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}), |
397 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
398 | + 'package_name': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}), |
399 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
400 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}) |
401 | + }, |
402 | + 'reviewsapp.softwareiteminorigin': { |
403 | + 'Meta': {'unique_together': "(('softwareitem', 'origin'),)"}, |
404 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}), |
405 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
406 | + 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}), |
407 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
408 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
409 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}) |
410 | + }, |
411 | + 'reviewsapp.softwareiteminrepository': { |
412 | + 'Meta': {'unique_together': "(('softwareitem', 'repository'),)"}, |
413 | + 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}), |
414 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
415 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}), |
416 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
417 | + 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}), |
418 | + 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}) |
419 | + }, |
420 | + 'reviewsapp.token': { |
421 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}), |
422 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
423 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
424 | + 'token': ('django.db.models.fields.CharField', [], {'default': "'mwoIqDbtCeQyFKvSAnleoBTvXpZzbKjDdkxAmBxsqpuAftkRvt'", 'max_length': '50', 'primary_key': 'True'}), |
425 | + 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'wTeEOEKkKzbDsJPtEUszuDgiDToSnKlTZjnOIrDlWXYKNkDkSr'", 'max_length': '50'}), |
426 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
427 | + }, |
428 | + 'reviewsapp.usefulness': { |
429 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
430 | + 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}), |
431 | + 'useful': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
432 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) |
433 | + } |
434 | + } |
435 | + |
436 | + complete_apps = ['reviewsapp'] |
437 | |
438 | === modified file 'src/reviewsapp/models/reviews.py' |
439 | --- src/reviewsapp/models/reviews.py 2011-03-18 07:29:55 +0000 |
440 | +++ src/reviewsapp/models/reviews.py 2011-03-18 17:09:28 +0000 |
441 | @@ -307,6 +307,11 @@ |
442 | usefulness_total = models.IntegerField(default=0) |
443 | usefulness_favorable = models.IntegerField(default=0) |
444 | usefulness_percentage = models.IntegerField(default=0) |
445 | + # XXX 2011-03-18 michaeln Remove application_name eventually. |
446 | + # After removing the app_name attribute of software item, we add it |
447 | + # here on the review so that we're not losing any information being |
448 | + # passed by the client. |
449 | + app_name = models.CharField(max_length=100, default='', blank=True) |
450 | |
451 | def __unicode__(self): |
452 | return u'lang: {0}, rating: {1}, summary: {2}\n{3}'.format( |
453 | @@ -322,9 +327,6 @@ |
454 | def package_name(self): |
455 | return self.softwareitem.package_name |
456 | |
457 | - def app_name(self): |
458 | - return self.softwareitem.app_name |
459 | - |
460 | def origin(self): |
461 | return self.repository.origin |
462 | |
463 | @@ -332,7 +334,7 @@ |
464 | return self.repository.distroseries |
465 | |
466 | def package_app_name(self): |
467 | - app_name = self.app_name() |
468 | + app_name = self.app_name |
469 | if app_name: |
470 | return self.package_name() + "/" + app_name |
471 | else: |
472 | |
473 | === modified file 'src/reviewsapp/tests/factory.py' |
474 | --- src/reviewsapp/tests/factory.py 2011-03-17 09:21:32 +0000 |
475 | +++ src/reviewsapp/tests/factory.py 2011-03-18 17:09:28 +0000 |
476 | @@ -160,7 +160,7 @@ |
477 | def makeReview(self, reviewer=None, software_item=None, repository=None, |
478 | version=None, rating="5", summary=None, review_text=None, |
479 | date_created=None, language=None, hide=False, num_flags=0, |
480 | - architecture='i386'): |
481 | + architecture='i386', app_name=''): |
482 | if reviewer is None: |
483 | reviewer = self.makeUser() |
484 | if software_item is None: |
485 | @@ -185,7 +185,8 @@ |
486 | softwareitem=software_item, repository=repository, |
487 | version=version, rating=rating, summary=summary, |
488 | review_text=review_text, language=language, hide=hide, |
489 | - date_created=date_created, architecture=architecture) |
490 | + date_created=date_created, architecture=architecture, |
491 | + app_name=app_name) |
492 | review.softwareitem_in_repository.update_stats() |
493 | for flag_count in range(num_flags): |
494 | review.flag( |
495 | |
496 | === modified file 'src/reviewsapp/tests/test_handlers.py' |
497 | --- src/reviewsapp/tests/test_handlers.py 2011-03-17 11:52:10 +0000 |
498 | +++ src/reviewsapp/tests/test_handlers.py 2011-03-18 17:09:28 +0000 |
499 | @@ -69,7 +69,7 @@ |
500 | def _get_url_kwargs_dict_for_review(review): |
501 | # this is the quoting we expect |
502 | appname=quote_plus(quote_plus( |
503 | - ";"+review.softwareitem.app_name.encode("utf-8"))) |
504 | + ";"+review.app_name.encode("utf-8"))) |
505 | pkgname=review.softwareitem.package_name |
506 | return dict(language=review.language, |
507 | origin=review.repository.origin, |
508 | @@ -332,8 +332,8 @@ |
509 | |
510 | def test_review_for_app(self): |
511 | review = self.factory.makeReview( |
512 | - software_item=self.factory.makeSoftwareItem('package_foo', |
513 | - app_name='app_foo')) |
514 | + software_item=self.factory.makeSoftwareItem('package_foo'), |
515 | + app_name='app_foo') |
516 | handler = ShowReviewsHandler() |
517 | |
518 | reviews = handler.read(None, 'any', 'any', 'any', 'any', |
519 | @@ -345,15 +345,15 @@ |
520 | def test_review_for_app_url(self): |
521 | # test utf8 encoded app |
522 | review = self.factory.makeReview( |
523 | - software_item=self.factory.makeSoftwareItem('package_foo', |
524 | - app_name=u'Déjà Dup')) |
525 | + software_item=self.factory.makeSoftwareItem('package_foo'), |
526 | + app_name=u'Déjà Dup') |
527 | url = reverse( |
528 | 'rnr-api-reviews-for-package', |
529 | kwargs=_get_url_kwargs_dict_for_review(review)) |
530 | response = self.client.get(url) |
531 | self.assertEqual(response.status_code, 200) |
532 | reviews = simplejson.loads(response.content) |
533 | - self.assertEqual(reviews[0]["app_name"], review.app_name()) |
534 | + self.assertEqual(reviews[0]["app_name"], review.app_name) |
535 | self.assertEqual(reviews[0]["package_name"], review.package_name()) |
536 | |
537 | # now repeat with blender that contains a () |
538 | @@ -366,7 +366,7 @@ |
539 | response = self.client.get(url) |
540 | self.assertEqual(response.status_code, 200) |
541 | reviews = simplejson.loads(response.content) |
542 | - self.assertEqual(reviews[0]["app_name"], review.app_name()) |
543 | + self.assertEqual(reviews[0]["app_name"], review.app_name) |
544 | self.assertEqual(reviews[0]["package_name"], review.package_name()) |
545 | |
546 | # now repeat with a fake app that contains a ";" |
547 | @@ -379,7 +379,7 @@ |
548 | response = self.client.get(url) |
549 | self.assertEqual(response.status_code, 200) |
550 | reviews = simplejson.loads(response.content) |
551 | - self.assertEqual(reviews[0]["app_name"], review.app_name()) |
552 | + self.assertEqual(reviews[0]["app_name"], review.app_name) |
553 | self.assertEqual(reviews[0]["package_name"], review.package_name()) |
554 | |
555 | def test_review_for_language(self): |
556 | |
557 | === modified file 'src/reviewsapp/tests/test_rnrclient.py' |
558 | --- src/reviewsapp/tests/test_rnrclient.py 2011-03-17 11:52:10 +0000 |
559 | +++ src/reviewsapp/tests/test_rnrclient.py 2011-03-18 17:09:28 +0000 |
560 | @@ -147,7 +147,7 @@ |
561 | |
562 | self.assertEqual(1, len(reviews)) |
563 | self.assertEqual(review.package_name(), reviews[0].package_name) |
564 | - self.assertEqual(review.app_name(), reviews[0].app_name) |
565 | + self.assertEqual(review.app_name, reviews[0].app_name) |
566 | self.assertEqual(review.summary, reviews[0].summary) |
567 | self.assertEqual(review.version, reviews[0].version) |
568 | self.assertEqual(review.reviewer_username(), |
569 | @@ -166,13 +166,13 @@ |
570 | origin=review.repository.origin, |
571 | distroseries=review.repository.distroseries, |
572 | packagename=review.package_name(), |
573 | - appname=review.app_name(), |
574 | + appname=str(review.app_name), |
575 | version=review.version |
576 | ) |
577 | |
578 | self.assertEqual(1, len(reviews)) |
579 | self.assertEqual(review.package_name(), reviews[0].package_name) |
580 | - self.assertEqual(review.app_name(), reviews[0].app_name) |
581 | + self.assertEqual(review.app_name, reviews[0].app_name) |
582 | self.assertEqual(review.summary, reviews[0].summary) |
583 | self.assertEqual(review.version, reviews[0].version) |
584 |