Merge lp:~michael.nelson/rnr-server/dont-depend-on-appname into lp:rnr-server

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
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.app_name -> Review.app_name including removing/creating relevant software items (forwards/backwards respectively).

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://pastebin.ubuntu.com/582130/

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://code.edge.launchpad.net/~michael.nelson/rnr-server/dont-depend-on-appname-2/+merge/54042
removes the actual SoftwareItem.app_name.

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
=== modified file 'src/reviewsapp/api/handlers.py'
--- src/reviewsapp/api/handlers.py 2011-03-17 14:29:54 +0000
+++ src/reviewsapp/api/handlers.py 2011-03-18 17:09:28 +0000
@@ -191,7 +191,7 @@
191 kwargs = dict(item for item in kwargs.items() if item[1] != 'any')191 kwargs = dict(item for item in kwargs.items() if item[1] != 'any')
192 return Review.objects.filter(192 return Review.objects.filter(
193 softwareitem__package_name=package_name,193 softwareitem__package_name=package_name,
194 softwareitem__app_name=appname,194 app_name=appname,
195 hide=False,195 hide=False,
196 **kwargs).order_by(196 **kwargs).order_by(
197 '-usefulness_percentage')[batch_start:batch_end]197 '-usefulness_percentage')[batch_start:batch_end]
198198
=== modified file 'src/reviewsapp/management/commands/populate.py'
--- src/reviewsapp/management/commands/populate.py 2011-03-15 14:08:02 +0000
+++ src/reviewsapp/management/commands/populate.py 2011-03-18 17:09:28 +0000
@@ -182,7 +182,7 @@
182 Currently (id, softwareitem_id, version, architecture_id,182 Currently (id, softwareitem_id, version, architecture_id,
183 repository_id, reviewer_id, date_created, rating, summary,183 repository_id, reviewer_id, date_created, rating, summary,
184 review_text, language, hide, usefulness_total,184 review_text, language, hide, usefulness_total,
185 usefulness_favorable, usefulness_percentage)185 usefulness_favorable, usefulness_percentage, app_name)
186 """186 """
187 softwareitem_id = randint(*self.ranges['reviewsapp_softwareitem'])187 softwareitem_id = randint(*self.ranges['reviewsapp_softwareitem'])
188 version = '%.1f' % random()188 version = '%.1f' % random()
@@ -195,5 +195,5 @@
195 review_text = self.random_comment(wordcount=20)195 review_text = self.random_comment(wordcount=20)
196 false = self.booleans[0]196 false = self.booleans[0]
197 return [id, softwareitem_id, version, arch_id, repo_id, reviewer_id,197 return [id, softwareitem_id, version, arch_id, repo_id, reviewer_id,
198 date_created, rating, summary, review_text, 'en', false, 0, 0, 0]198 date_created, rating, summary, review_text, 'en', false, 0, 0, 0, '']
199199
200200
=== added file 'src/reviewsapp/migrations/0006_add_review_app_name.py'
--- src/reviewsapp/migrations/0006_add_review_app_name.py 1970-01-01 00:00:00 +0000
+++ src/reviewsapp/migrations/0006_add_review_app_name.py 2011-03-18 17:09:28 +0000
@@ -0,0 +1,160 @@
1
2from south.db import db
3from django.db import models
4from reviewsapp.models import *
5
6class Migration:
7
8 def forwards(self, orm):
9
10 # Adding field 'Review.app_name'
11 db.add_column('reviewsapp_review', 'app_name', orm['reviewsapp.review:app_name'])
12
13 def backwards(self, orm):
14
15 # Deleting field 'Review.app_name'
16 db.delete_column('reviewsapp_review', 'app_name')
17
18
19 models = {
20 'auth.group': {
21 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
22 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
23 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
24 },
25 'auth.permission': {
26 'Meta': {'unique_together': "(('content_type', 'codename'),)"},
27 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
28 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
29 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
31 },
32 'auth.user': {
33 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
34 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
35 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
36 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
37 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
39 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
40 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
41 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
42 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
43 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
44 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
45 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
46 },
47 'contenttypes.contenttype': {
48 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
49 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
50 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
52 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
53 },
54 'reviewsapp.architecture': {
55 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 'tag': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'})
57 },
58 'reviewsapp.consumer': {
59 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
60 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
61 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
62 'secret': ('django.db.models.fields.CharField', [], {'default': "'OzuURCXRwhChWIMXbLbcgooiStFeze'", 'max_length': '255', 'blank': 'True'}),
63 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
64 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
65 },
66 'reviewsapp.nonce': {
67 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}),
68 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
69 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
70 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
71 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Token']"})
72 },
73 'reviewsapp.repository': {
74 'distroseries': ('django.db.models.fields.SlugField', [], {'max_length': '25', 'db_index': 'True'}),
75 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
76 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'})
77 },
78 'reviewsapp.review': {
79 'app_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
80 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Architecture']"}),
81 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 823060)'}),
82 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
83 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84 'language': ('django.db.models.fields.SlugField', [], {'max_length': '10', 'db_index': 'True'}),
85 'rating': ('django.db.models.fields.IntegerField', [], {}),
86 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}),
87 'review_text': ('django.db.models.fields.CharField', [], {'max_length': '5000'}),
88 'reviewer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
89 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}),
90 'summary': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
91 'usefulness_favorable': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
92 'usefulness_percentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
93 'usefulness_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
94 'version': ('django.db.models.fields.CharField', [], {'max_length': '100'})
95 },
96 'reviewsapp.reviewmoderation': {
97 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 770486)'}),
98 'date_moderated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
99 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
100 'moderation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
101 'moderator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
102 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}),
103 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'})
104 },
105 'reviewsapp.reviewmoderationflag': {
106 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 25, 569414)'}),
107 'description': ('django.db.models.fields.TextField', [], {}),
108 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
109 'review_moderation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.ReviewModeration']"}),
110 'summary': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
111 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
112 },
113 'reviewsapp.rnrsettings': {
114 'blacklist_words': ('django.db.models.fields.TextField', [], {'default': "''"}),
115 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
116 'moderation_mode': ('django.db.models.fields.CharField', [], {'default': "'passive'", 'max_length': '8'})
117 },
118 'reviewsapp.softwareitem': {
119 'app_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
120 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}),
121 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
122 'package_name': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}),
123 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
124 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'})
125 },
126 'reviewsapp.softwareiteminorigin': {
127 'Meta': {'unique_together': "(('softwareitem', 'origin'),)"},
128 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}),
129 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
130 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}),
131 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
132 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
133 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"})
134 },
135 'reviewsapp.softwareiteminrepository': {
136 'Meta': {'unique_together': "(('softwareitem', 'repository'),)"},
137 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 11, 24, 339806)'}),
138 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
140 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
141 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}),
142 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"})
143 },
144 'reviewsapp.token': {
145 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}),
146 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
147 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
148 'token': ('django.db.models.fields.CharField', [], {'default': "'LXGJOoldWHCXXULnsTXhHOGmAaKCYUrXJRRgrfUjMaJfObdlEU'", 'max_length': '50', 'primary_key': 'True'}),
149 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'MnRkFdkPZXmIqCKNExfyzqdWifIWiWaoZGQKNdtoounoziJVsU'", 'max_length': '50'}),
150 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
151 },
152 'reviewsapp.usefulness': {
153 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}),
155 'useful': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
156 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
157 }
158 }
159
160 complete_apps = ['reviewsapp']
0161
=== added file 'src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py'
--- src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py 1970-01-01 00:00:00 +0000
+++ src/reviewsapp/migrations/0007_populate_review_app_name_and_remove_redundant_software_items.py 2011-03-18 17:09:28 +0000
@@ -0,0 +1,234 @@
1
2from south.db import db
3from django.db import models
4from reviewsapp.models import *
5
6class Migration:
7
8 def forwards(self, orm):
9 """Copy the app name from the software item and update the link."""
10 # For each software_item, we want to update all the related
11 # reviews.
12 for software_item in orm['reviewsapp.softwareitem'].objects.all():
13 software_item.review_set.update(
14 app_name=software_item.app_name)
15
16 # Next, we ensure that all the reviews for a given packagename
17 # refer to a single software_item, and delete the redundant
18 # softawre_items.
19 package_names = orm['reviewsapp.softwareitem'].objects.values_list(
20 'package_name', flat=True)
21 package_names = package_names.order_by('package_name').distinct()
22 for package_name in package_names:
23 # Grab all the reviews for a given package name and update
24 # them to all point to the one software item.
25 reviews = orm['reviewsapp.review'].objects.filter(
26 softwareitem__package_name=package_name)
27 first_review = reviews[0]
28 remaining_software_item = reviews[0].softwareitem
29 reviews.update(softwareitem=remaining_software_item)
30
31 # Now delete all the other software items with the same
32 # package name (and software item in origins /
33 # repositories).
34 orm['reviewsapp.softwareitem'].objects.filter(
35 package_name=package_name).exclude(
36 id=remaining_software_item.id).delete()
37
38 # Finally, we need to ensure that each review corresponds
39 # to a valid SoftwareItemInRepository so that stats can be
40 # calculated.
41 self.ensure_softwareitem_in_repos(orm, remaining_software_item)
42
43 def backwards(self, orm):
44 """Create the software items with app names and update reviews."""
45 SoftwareItemClass = orm['reviewsapp.softwareitem']
46 for software_item in SoftwareItemClass.objects.all():
47 app_names = software_item.review_set.values_list(
48 'app_name', flat=True).distinct()
49 software_item.app_name = app_names[0]
50 software_item.save()
51 package_name = software_item.package_name
52 # We need to recreate the repository/origin stats for
53 # the existing softwareitem.
54 software_item.softwareiteminrepository_set.all().delete()
55 software_item.softwareiteminorigin_set.all().delete()
56
57 # For any other app names, we create a new software item and
58 # point the relevant reviews to it.
59 for app_name in app_names:
60 new_item, created = SoftwareItemClass.objects.get_or_create(
61 package_name=package_name,
62 app_name=app_name)
63
64 orm['reviewsapp.review'].objects.filter(
65 softwareitem=software_item,
66 app_name=app_name).update(
67 softwareitem=new_item)
68
69 # Once all review.software_items have been updated, we can
70 # create the related items in repos etc.
71 for item in SoftwareItemClass.objects.all():
72 self.ensure_softwareitem_in_repos(orm, item)
73
74 # Finally, blank out the Review.app_name field.
75 orm['reviewsapp.review'].objects.all().update(app_name='')
76
77 def ensure_softwareitem_in_repos(self, orm, software_item):
78 # Find all reviews referencing the item and get a list of
79 # distint repositories.
80 reviews = orm['reviewsapp.review'].objects.filter(
81 softwareitem=software_item)
82 repository_ids = reviews.values_list('repository', flat=True).distinct()
83 SIIRepo = orm['reviewsapp.softwareiteminrepository']
84 SIIOrigin = orm['reviewsapp.softwareiteminorigin']
85 for repo_id in repository_ids:
86 repo = orm['reviewsapp.repository'].objects.get(id=repo_id)
87 item_in_repo, created = SIIRepo.objects.get_or_create(
88 softwareitem=software_item, repository=repo)
89 item_in_origin, created = SIIOrigin.objects.get_or_create(
90 softwareitem=software_item, origin=repo.origin)
91
92
93 models = {
94 'auth.group': {
95 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
97 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
98 },
99 'auth.permission': {
100 'Meta': {'unique_together': "(('content_type', 'codename'),)"},
101 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
102 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
103 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
105 },
106 'auth.user': {
107 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
108 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
109 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
110 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
111 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
112 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
113 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
114 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
115 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
116 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
117 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
118 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
119 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
120 },
121 'contenttypes.contenttype': {
122 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
123 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
124 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
125 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
126 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
127 },
128 'reviewsapp.architecture': {
129 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
130 'tag': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'})
131 },
132 'reviewsapp.consumer': {
133 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
134 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
135 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
136 'secret': ('django.db.models.fields.CharField', [], {'default': "'qBxqBnulneXJvOyccXpAJdJmhzatIw'", 'max_length': '255', 'blank': 'True'}),
137 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
138 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
139 },
140 'reviewsapp.nonce': {
141 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}),
142 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
143 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
145 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Token']"})
146 },
147 'reviewsapp.repository': {
148 'distroseries': ('django.db.models.fields.SlugField', [], {'max_length': '25', 'db_index': 'True'}),
149 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
150 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'})
151 },
152 'reviewsapp.review': {
153 'app_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
154 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Architecture']"}),
155 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 823749)'}),
156 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
157 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
158 'language': ('django.db.models.fields.SlugField', [], {'max_length': '10', 'db_index': 'True'}),
159 'rating': ('django.db.models.fields.IntegerField', [], {}),
160 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}),
161 'review_text': ('django.db.models.fields.CharField', [], {'max_length': '5000'}),
162 'reviewer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
163 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"}),
164 'summary': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
165 'usefulness_favorable': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
166 'usefulness_percentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
167 'usefulness_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
168 'version': ('django.db.models.fields.CharField', [], {'max_length': '100'})
169 },
170 'reviewsapp.reviewmoderation': {
171 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 959691)'}),
172 'date_moderated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
173 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
174 'moderation_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
175 'moderator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
176 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}),
177 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'})
178 },
179 'reviewsapp.reviewmoderationflag': {
180 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 12, 530560)'}),
181 'description': ('django.db.models.fields.TextField', [], {}),
182 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183 'review_moderation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.ReviewModeration']"}),
184 'summary': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
185 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
186 },
187 'reviewsapp.rnrsettings': {
188 'blacklist_words': ('django.db.models.fields.TextField', [], {'default': "''"}),
189 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
190 'moderation_mode': ('django.db.models.fields.CharField', [], {'default': "'passive'", 'max_length': '8'})
191 },
192 'reviewsapp.softwareitem': {
193 'app_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True', 'default': "''"}),
194 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}),
195 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
196 'package_name': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}),
197 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
198 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'})
199 },
200 'reviewsapp.softwareiteminorigin': {
201 'Meta': {'unique_together': "(('softwareitem', 'origin'),)"},
202 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}),
203 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
204 'origin': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'db_index': 'True'}),
205 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
206 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
207 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"})
208 },
209 'reviewsapp.softwareiteminrepository': {
210 'Meta': {'unique_together': "(('softwareitem', 'repository'),)"},
211 'date_ratings_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 18, 11, 13, 11, 529267)'}),
212 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
213 'ratings_average': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '3', 'decimal_places': '2'}),
214 'ratings_total': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
215 'repository': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Repository']"}),
216 'softwareitem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.SoftwareItem']"})
217 },
218 'reviewsapp.token': {
219 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Consumer']"}),
220 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
221 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
222 'token': ('django.db.models.fields.CharField', [], {'default': "'mwoIqDbtCeQyFKvSAnleoBTvXpZzbKjDdkxAmBxsqpuAftkRvt'", 'max_length': '50', 'primary_key': 'True'}),
223 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'wTeEOEKkKzbDsJPtEUszuDgiDToSnKlTZjnOIrDlWXYKNkDkSr'", 'max_length': '50'}),
224 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
225 },
226 'reviewsapp.usefulness': {
227 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
228 'review': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['reviewsapp.Review']"}),
229 'useful': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
230 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
231 }
232 }
233
234 complete_apps = ['reviewsapp']
0235
=== modified file 'src/reviewsapp/models/reviews.py'
--- src/reviewsapp/models/reviews.py 2011-03-18 07:29:55 +0000
+++ src/reviewsapp/models/reviews.py 2011-03-18 17:09:28 +0000
@@ -307,6 +307,11 @@
307 usefulness_total = models.IntegerField(default=0)307 usefulness_total = models.IntegerField(default=0)
308 usefulness_favorable = models.IntegerField(default=0)308 usefulness_favorable = models.IntegerField(default=0)
309 usefulness_percentage = models.IntegerField(default=0)309 usefulness_percentage = models.IntegerField(default=0)
310 # XXX 2011-03-18 michaeln Remove application_name eventually.
311 # After removing the app_name attribute of software item, we add it
312 # here on the review so that we're not losing any information being
313 # passed by the client.
314 app_name = models.CharField(max_length=100, default='', blank=True)
310315
311 def __unicode__(self):316 def __unicode__(self):
312 return u'lang: {0}, rating: {1}, summary: {2}\n{3}'.format(317 return u'lang: {0}, rating: {1}, summary: {2}\n{3}'.format(
@@ -322,9 +327,6 @@
322 def package_name(self):327 def package_name(self):
323 return self.softwareitem.package_name328 return self.softwareitem.package_name
324329
325 def app_name(self):
326 return self.softwareitem.app_name
327
328 def origin(self):330 def origin(self):
329 return self.repository.origin331 return self.repository.origin
330332
@@ -332,7 +334,7 @@
332 return self.repository.distroseries334 return self.repository.distroseries
333335
334 def package_app_name(self):336 def package_app_name(self):
335 app_name = self.app_name()337 app_name = self.app_name
336 if app_name:338 if app_name:
337 return self.package_name() + "/" + app_name339 return self.package_name() + "/" + app_name
338 else:340 else:
339341
=== modified file 'src/reviewsapp/tests/factory.py'
--- src/reviewsapp/tests/factory.py 2011-03-17 09:21:32 +0000
+++ src/reviewsapp/tests/factory.py 2011-03-18 17:09:28 +0000
@@ -160,7 +160,7 @@
160 def makeReview(self, reviewer=None, software_item=None, repository=None,160 def makeReview(self, reviewer=None, software_item=None, repository=None,
161 version=None, rating="5", summary=None, review_text=None,161 version=None, rating="5", summary=None, review_text=None,
162 date_created=None, language=None, hide=False, num_flags=0,162 date_created=None, language=None, hide=False, num_flags=0,
163 architecture='i386'):163 architecture='i386', app_name=''):
164 if reviewer is None:164 if reviewer is None:
165 reviewer = self.makeUser()165 reviewer = self.makeUser()
166 if software_item is None:166 if software_item is None:
@@ -185,7 +185,8 @@
185 softwareitem=software_item, repository=repository,185 softwareitem=software_item, repository=repository,
186 version=version, rating=rating, summary=summary,186 version=version, rating=rating, summary=summary,
187 review_text=review_text, language=language, hide=hide,187 review_text=review_text, language=language, hide=hide,
188 date_created=date_created, architecture=architecture)188 date_created=date_created, architecture=architecture,
189 app_name=app_name)
189 review.softwareitem_in_repository.update_stats()190 review.softwareitem_in_repository.update_stats()
190 for flag_count in range(num_flags):191 for flag_count in range(num_flags):
191 review.flag(192 review.flag(
192193
=== modified file 'src/reviewsapp/tests/test_handlers.py'
--- src/reviewsapp/tests/test_handlers.py 2011-03-17 11:52:10 +0000
+++ src/reviewsapp/tests/test_handlers.py 2011-03-18 17:09:28 +0000
@@ -69,7 +69,7 @@
69def _get_url_kwargs_dict_for_review(review):69def _get_url_kwargs_dict_for_review(review):
70 # this is the quoting we expect70 # this is the quoting we expect
71 appname=quote_plus(quote_plus(71 appname=quote_plus(quote_plus(
72 ";"+review.softwareitem.app_name.encode("utf-8")))72 ";"+review.app_name.encode("utf-8")))
73 pkgname=review.softwareitem.package_name73 pkgname=review.softwareitem.package_name
74 return dict(language=review.language,74 return dict(language=review.language,
75 origin=review.repository.origin,75 origin=review.repository.origin,
@@ -332,8 +332,8 @@
332332
333 def test_review_for_app(self):333 def test_review_for_app(self):
334 review = self.factory.makeReview(334 review = self.factory.makeReview(
335 software_item=self.factory.makeSoftwareItem('package_foo',335 software_item=self.factory.makeSoftwareItem('package_foo'),
336 app_name='app_foo'))336 app_name='app_foo')
337 handler = ShowReviewsHandler()337 handler = ShowReviewsHandler()
338338
339 reviews = handler.read(None, 'any', 'any', 'any', 'any',339 reviews = handler.read(None, 'any', 'any', 'any', 'any',
@@ -345,15 +345,15 @@
345 def test_review_for_app_url(self):345 def test_review_for_app_url(self):
346 # test utf8 encoded app346 # test utf8 encoded app
347 review = self.factory.makeReview(347 review = self.factory.makeReview(
348 software_item=self.factory.makeSoftwareItem('package_foo',348 software_item=self.factory.makeSoftwareItem('package_foo'),
349 app_name=u'Déjà Dup'))349 app_name=u'Déjà Dup')
350 url = reverse(350 url = reverse(
351 'rnr-api-reviews-for-package',351 'rnr-api-reviews-for-package',
352 kwargs=_get_url_kwargs_dict_for_review(review))352 kwargs=_get_url_kwargs_dict_for_review(review))
353 response = self.client.get(url)353 response = self.client.get(url)
354 self.assertEqual(response.status_code, 200)354 self.assertEqual(response.status_code, 200)
355 reviews = simplejson.loads(response.content)355 reviews = simplejson.loads(response.content)
356 self.assertEqual(reviews[0]["app_name"], review.app_name())356 self.assertEqual(reviews[0]["app_name"], review.app_name)
357 self.assertEqual(reviews[0]["package_name"], review.package_name())357 self.assertEqual(reviews[0]["package_name"], review.package_name())
358358
359 # now repeat with blender that contains a ()359 # now repeat with blender that contains a ()
@@ -366,7 +366,7 @@
366 response = self.client.get(url)366 response = self.client.get(url)
367 self.assertEqual(response.status_code, 200)367 self.assertEqual(response.status_code, 200)
368 reviews = simplejson.loads(response.content)368 reviews = simplejson.loads(response.content)
369 self.assertEqual(reviews[0]["app_name"], review.app_name())369 self.assertEqual(reviews[0]["app_name"], review.app_name)
370 self.assertEqual(reviews[0]["package_name"], review.package_name())370 self.assertEqual(reviews[0]["package_name"], review.package_name())
371371
372 # now repeat with a fake app that contains a ";"372 # now repeat with a fake app that contains a ";"
@@ -379,7 +379,7 @@
379 response = self.client.get(url)379 response = self.client.get(url)
380 self.assertEqual(response.status_code, 200)380 self.assertEqual(response.status_code, 200)
381 reviews = simplejson.loads(response.content)381 reviews = simplejson.loads(response.content)
382 self.assertEqual(reviews[0]["app_name"], review.app_name())382 self.assertEqual(reviews[0]["app_name"], review.app_name)
383 self.assertEqual(reviews[0]["package_name"], review.package_name())383 self.assertEqual(reviews[0]["package_name"], review.package_name())
384384
385 def test_review_for_language(self):385 def test_review_for_language(self):
386386
=== modified file 'src/reviewsapp/tests/test_rnrclient.py'
--- src/reviewsapp/tests/test_rnrclient.py 2011-03-17 11:52:10 +0000
+++ src/reviewsapp/tests/test_rnrclient.py 2011-03-18 17:09:28 +0000
@@ -147,7 +147,7 @@
147147
148 self.assertEqual(1, len(reviews))148 self.assertEqual(1, len(reviews))
149 self.assertEqual(review.package_name(), reviews[0].package_name)149 self.assertEqual(review.package_name(), reviews[0].package_name)
150 self.assertEqual(review.app_name(), reviews[0].app_name)150 self.assertEqual(review.app_name, reviews[0].app_name)
151 self.assertEqual(review.summary, reviews[0].summary)151 self.assertEqual(review.summary, reviews[0].summary)
152 self.assertEqual(review.version, reviews[0].version)152 self.assertEqual(review.version, reviews[0].version)
153 self.assertEqual(review.reviewer_username(),153 self.assertEqual(review.reviewer_username(),
@@ -166,13 +166,13 @@
166 origin=review.repository.origin,166 origin=review.repository.origin,
167 distroseries=review.repository.distroseries,167 distroseries=review.repository.distroseries,
168 packagename=review.package_name(),168 packagename=review.package_name(),
169 appname=review.app_name(),169 appname=str(review.app_name),
170 version=review.version170 version=review.version
171 )171 )
172172
173 self.assertEqual(1, len(reviews))173 self.assertEqual(1, len(reviews))
174 self.assertEqual(review.package_name(), reviews[0].package_name)174 self.assertEqual(review.package_name(), reviews[0].package_name)
175 self.assertEqual(review.app_name(), reviews[0].app_name)175 self.assertEqual(review.app_name, reviews[0].app_name)
176 self.assertEqual(review.summary, reviews[0].summary)176 self.assertEqual(review.summary, reviews[0].summary)
177 self.assertEqual(review.version, reviews[0].version)177 self.assertEqual(review.version, reviews[0].version)
178178

Subscribers

People subscribed via source and target branches