reserve the slugs "favorites" and "mobile" (bug 586426)

Also adding a unique key on (author, slug) and fixing up tests.
This commit is contained in:
Jeff Balogh 2010-08-12 13:06:57 -07:00
Родитель fab5aceeae
Коммит 75b42a47d6
7 изменённых файлов: 78 добавлений и 55 удалений

Просмотреть файл

@ -145,24 +145,3 @@ def collections_add_slugs():
c.slug = 'collection'
c.save(force_update=True)
task_log.info(u'%s. %s => %s' % (next(cnt), c.name, c.slug))
# Uniquify slug names by user.
cursor = connections[multidb.get_slave()].cursor()
dupes = cursor.execute("""
SELECT user_id, slug FROM (
SELECT user_id, slug, COUNT(1) AS cnt
FROM collections c INNER JOIN collections_users cu
ON c.id = cu.collection_id
GROUP BY user_id, slug) j
WHERE j.cnt > 1""")
task_log.info('Uniquifying %s (user, slug) pairs' % dupes)
cnt = itertools.count()
for user, slug in cursor.fetchall():
q = Collection.objects.filter(slug=slug, collectionuser__user=user)
# Skip the first one since it's unique without any appendage.
for idx, c in enumerate(q[1:]):
# Give enough space for appending a two-digit number.
slug = c.slug[:max_length - 3]
c.slug = u'%s-%s' % (slug, idx + 1)
c.save(force_update=True)
task_log.info(u'%s. %s => %s' % (next(cnt), slug, c.slug))

Просмотреть файл

@ -20,6 +20,12 @@ from users.models import UserProfile
from translations.fields import TranslatedField, LinkifiedField
SPECIAL_SLUGS = {
'mobile': amo.COLLECTION_MOBILE,
'favorites': amo.COLLECTION_FAVORITES,
}
class TopTags(object):
"""Descriptor to manage a collection's top tags in cache."""
@ -108,6 +114,7 @@ class Collection(amo.models.ModelBase):
class Meta(amo.models.ModelBase.Meta):
db_table = 'collections'
unique_together = (('author', 'slug'),)
def __unicode__(self):
return u'%s (%s)' % (self.name, self.addon_count)
@ -117,6 +124,7 @@ class Collection(amo.models.ModelBase):
self.uuid = unicode(uuid.uuid4())
if not self.slug:
self.slug = self.uuid[:30]
self.clean_slug()
# Maintain our index of add-on ids.
if self.id:
@ -125,6 +133,21 @@ class Collection(amo.models.ModelBase):
super(Collection, self).save(**kw)
def clean_slug(self):
if (self.slug in SPECIAL_SLUGS and
self.type != SPECIAL_SLUGS[self.slug]):
self.slug += '~'
if not self.author:
return
qs = self.author.collections.using('default')
slugs = dict((slug, id) for slug, id in qs.values_list('slug', 'id'))
if self.slug in slugs and slugs[self.slug] != self.id:
for idx in range(len(slugs)):
new = '%s-%s' % (self.slug, idx + 1)
if new not in slugs:
self.slug = new
return
def get_url_path(self):
if settings.NEW_COLLECTIONS:
return reverse('collections.detail',

Просмотреть файл

@ -4,6 +4,7 @@ from pyquery import PyQuery as pq
from amo.urlresolvers import reverse
from bandwagon.models import Collection
from users.models import UserProfile
class AjaxTest(test_utils.TestCase):
@ -12,9 +13,10 @@ class AjaxTest(test_utils.TestCase):
'base/collections')
def setUp(self):
status = self.client.login(username='clouserw@gmail.com',
password='yermom')
assert status, "Didn't log in."
assert self.client.login(username='clouserw@gmail.com',
password='yermom')
self.user = UserProfile.objects.get(email='clouserw@gmail.com')
self.other = UserProfile.objects.exclude(id=self.user.id)[0]
def test_list_collections(self):
r = self.client.get(reverse('collections.ajax_list')
@ -54,7 +56,7 @@ class AjaxTest(test_utils.TestCase):
def test_add_other_collection(self):
"403 when you try to add to a collection that isn't yours."
c = Collection()
c = Collection(author=self.other)
c.save()
r = self.client.post(reverse('collections.ajax_add'),
@ -63,7 +65,7 @@ class AjaxTest(test_utils.TestCase):
def test_remove_other_collection(self):
"403 when you try to add to a collection that isn't yours."
c = Collection()
c = Collection(author=self.other)
c.save()
r = self.client.post(reverse('collections.ajax_remove'),

Просмотреть файл

@ -24,14 +24,12 @@ class TestHelpers(test.TestCase):
def setUp(self):
self.client.get('/')
self.user = UserProfile.objects.create(nickname='uniq', email='uniq')
def test_collection_favorite(self):
c = {}
u = UserProfile()
u.nickname = 'unique'
u.save()
c['request'] = Mock()
c['request'].amo_user = u
c['request'].amo_user = self.user
collection = Collection.objects.get(pk=80)
# Not subscribed yet.
@ -39,7 +37,7 @@ class TestHelpers(test.TestCase):
eq_(doc('button').text(), u'Add to Favorites')
# Subscribed.
collection.subscriptions.create(user=u)
collection.subscriptions.create(user=self.user)
doc = pq(collection_favorite(c, collection))
eq_(doc('button').text(), u'Remove from Favorites')
@ -66,9 +64,9 @@ class TestHelpers(test.TestCase):
reverse('collections.vote', args=['clouserw', 'mccrackin', 'up']))
def test_user_collection_list(self):
c1 = Collection.objects.create(
c1 = Collection.objects.create(author=self.user,
uuid='eb4e3cd8-5cf1-4832-86fb-a90fc6d3765c')
c2 = Collection.objects.create(
c2 = Collection.objects.create(author=self.user,
uuid='61780943-e159-4206-8acd-0ae9f63f294c',
nickname='my_collection')
heading = 'My Heading'

Просмотреть файл

@ -24,17 +24,18 @@ class TestCollections(test_utils.TestCase):
'bandwagon/test_models'
)
def setUp(self):
self.user = UserProfile.objects.create(nickname='uhhh', email='uh@hh')
self.other = UserProfile.objects.exclude(id=self.user.id)[0]
def test_icon_url(self):
c = Collection.objects.get(pk=512)
assert c.icon_url.endswith('img/amo2009/icons/collection.png')
def test_is_subscribed(self):
c = Collection.objects.get(pk=512)
u = UserProfile()
u.nickname = 'unique'
u.save()
c.subscriptions.create(user=u)
assert c.is_subscribed(u), "User isn't subscribed to collection."
c.subscriptions.create(user=self.user)
assert c.is_subscribed(self.user)
def test_translation_default(self):
"""Make sure we're getting strings from the default locale."""
@ -47,19 +48,20 @@ class TestCollections(test_utils.TestCase):
# make a private collection
private = Collection(
name="Hello", uuid="4e2a1acc-39ae-47ec-956f-46e080ac7f69",
listed=False)
listed=False, author=self.user)
private.save()
listed = Collection.objects.listed()
eq_(len(listed), listed_count)
def test_auto_uuid(self):
c = Collection.objects.create()
c = Collection.objects.create(author=self.user)
assert c.uuid != ''
assert isinstance(c.uuid, basestring)
def test_addon_index(self):
c = Collection.objects.get(pk=80)
c.author = self.user
eq_(c.addon_index, None)
ids = c.addons.values_list('id', flat=True)
c.save()
@ -67,17 +69,17 @@ class TestCollections(test_utils.TestCase):
def test_synced_collection(self):
"""SyncedCollections automatically get type=sync."""
c = SyncedCollection.objects.create()
c = SyncedCollection.objects.create(author=self.user)
eq_(c.type, amo.COLLECTION_SYNCHRONIZED)
def test_recommended_collection(self):
"""RecommendedCollections automatically get type=rec."""
c = RecommendedCollection.objects.create()
c = RecommendedCollection.objects.create(author=self.user)
eq_(c.type, amo.COLLECTION_RECOMMENDED)
def test_set_addons(self):
addons = list(Addon.objects.values_list('id', flat=True))
c = Collection.objects.create()
c = Collection.objects.create(author=self.user)
# Check insert.
random.shuffle(addons)
@ -96,26 +98,45 @@ class TestCollections(test_utils.TestCase):
eq_(c.addons.count(), len(addons))
def test_publishable_by(self):
c = Collection()
u = UserProfile(nickname='f')
c.save()
u.save()
CollectionUser(collection=c, user=u).save()
eq_(c.publishable_by(u), True)
c = Collection.objects.create(author=self.other)
CollectionUser(collection=c, user=self.user).save()
eq_(c.publishable_by(self.user), True)
def test_collection_meta(self):
c = Collection.objects.create()
c = Collection.objects.create(author=self.user)
eq_(c.addon_count, 0)
c.add_addon(Addon.objects.all()[0])
c = Collection.objects.get(id=c.id)
assert not c.from_cache
eq_(c.addon_count, 1)
def test_favorites_slug(self):
c = Collection.objects.create(author=self.user, slug='favorites')
eq_(c.type, amo.COLLECTION_NORMAL)
eq_(c.slug, 'favorites~')
c = Collection.objects.create(author=self.user, slug='favorites')
eq_(c.type, amo.COLLECTION_NORMAL)
eq_(c.slug, 'favorites~-1')
def test_slug_dupe(self):
c = Collection.objects.create(author=self.user, slug='boom')
eq_(c.slug, 'boom')
c.save()
eq_(c.slug, 'boom')
c = Collection.objects.create(author=self.user, slug='boom')
eq_(c.slug, 'boom-1')
c = Collection.objects.create(author=self.user, slug='boom')
eq_(c.slug, 'boom-2')
class TestRecommendations(test_utils.TestCase):
fixtures = ['base/addon-recs']
ids = [5299, 1843, 2464, 7661, 5369]
def setUp(self):
self.user = UserProfile.objects.create(nickname='uhhh', email='uh@hh')
def expected_recs(self):
scores, ranked = [], {}
# Get all the add-on => rank pairs.
@ -133,7 +154,7 @@ class TestRecommendations(test_utils.TestCase):
eq_(recs, self.expected_recs())
def test_get_recommendations(self):
c = Collection.objects.create()
c = Collection.objects.create(author=self.user)
c.set_addons(self.ids)
recs = c.get_recommendations()
eq_(recs.type, amo.COLLECTION_RECOMMENDED)

Просмотреть файл

@ -17,11 +17,10 @@ from addons.models import Addon
from addons.views import BaseFilter
from tags.models import Tag
from translations.query import order_by_translation
from .models import Collection, CollectionAddon, CollectionUser, CollectionVote
from .models import (Collection, CollectionAddon, CollectionUser,
CollectionVote, SPECIAL_SLUGS)
from . import forms
SPECIAL_COLLECTIONS = ['mobile', 'favorites']
log = commonware.log.getLogger('z.collections')
@ -258,7 +257,7 @@ def ajax_list(request):
@login_required
@post_required
def collection_alter(request, username, slug, action):
if slug in SPECIAL_COLLECTIONS:
if slug in SPECIAL_SLUGS:
c = getattr(request.amo_user, slug + '_collection')()
else:
c = get_object_or_404(Collection.objects,

Просмотреть файл

@ -0,0 +1 @@
ALTER TABLE collections ADD UNIQUE (`author_id`, `slug`);