# HG changeset patch -- Bitbucket.org # Project django-invitation # URL http://bitbucket.org/david/django-invitation/overview # User David Montgomery # Date 1244488776 25200 # Node ID 8d2a11dc61295ca8d979d29a75cd0aa41fe1dae0 # Parent b2977a099938e3d2d1c4d293effa77591e618c03 Added InvitationUser model to support explicit per-user invitations_remaining. See issue 4: http://bitbucket.org/david/django-invitation/issue/4/support-user-specific-number-of --- a/invitation/models.py +++ b/invitation/models.py @@ -52,8 +52,7 @@ class InvitationKeyManager(models.Manage """ Returns the number of remaining invitations for a given ``User``. """ - inviteds_count = self.filter(from_user=user).count() - return settings.INVITATIONS_PER_USER - inviteds_count + return InvitationUser.objects.get(inviter=user).invitations_remaining def delete_expired_keys(self): for key in self.all(): @@ -124,3 +123,31 @@ class InvitationKey(models.Model): send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [email]) +class InvitationUser(models.Model): + inviter = models.ForeignKey(User, unique=True) + invitations_remaining = models.IntegerField() + + def __unicode__(self): + return u"InvitationUser for %s" % self.inviter.username + + +def user_post_save(sender, instance, created, **kwargs): + """Create InvitationUser for user when User is created.""" + if created: + invitation_user = InvitationUser() + invitation_user.inviter = instance + invitation_user.invitations_remaining = settings.INVITATIONS_PER_USER + invitation_user.save() + +models.signals.post_save.connect(user_post_save, sender=User) + +def invitation_key_post_save(sender, instance, created, **kwargs): + """Decrement invitations_remaining when InvitationKey is created.""" + if created: + invitation_user = InvitationUser.objects.get(inviter=instance.from_user) + remaining = invitation_user.invitations_remaining + invitation_user.invitations_remaining = remaining-1 + invitation_user.save() + +models.signals.post_save.connect(invitation_key_post_save, sender=InvitationKey) + --- a/invitation/admin.py +++ b/invitation/admin.py @@ -1,7 +1,11 @@ from django.contrib import admin -from invitation.models import InvitationKey +from invitation.models import InvitationKey, InvitationUser class InvitationKeyAdmin(admin.ModelAdmin): list_display = ('__unicode__', 'from_user', 'date_invited', 'key_expired') +class InvitationUserAdmin(admin.ModelAdmin): + list_display = ('inviter', 'invitations_remaining') + admin.site.register(InvitationKey, InvitationKeyAdmin) +admin.site.register(InvitationUser, InvitationUserAdmin) --- a/invitation/tests.py +++ b/invitation/tests.py @@ -27,7 +27,7 @@ from django.core.urlresolvers import rev from django.test import TestCase from invitation import forms -from invitation.models import InvitationKey +from invitation.models import InvitationKey, InvitationUser class InvitationTestCase(TestCase): """ @@ -97,8 +97,32 @@ class InvitationModelTests(InvitationTes """ management.call_command('cleanupinvitation') self.assertEqual(InvitationKey.objects.count(), 1) + + def test_invitations_remaining(self): + """Test InvitationUser calculates remaining invitations properly.""" + remaining_invites = InvitationKey.objects.remaining_invitations_for_user + # New user starts with settings.INVITATIONS_PER_USER + user = User.objects.create_user(username='newbie', + password='secret', + email='newbie@example.com') + self.assertEqual(remaining_invites(user), settings.INVITATIONS_PER_USER) + # After using some, amount remaining is decreased + used = InvitationKey.objects.filter(from_user=self.sample_user).count() + expected_remaining = settings.INVITATIONS_PER_USER - used + remaining = remaining_invites(self.sample_user) + self.assertEqual(remaining, expected_remaining) + + # Using Invitationuser via Admin, remaining can be increased + invitation_user = InvitationUser.objects.get(inviter=self.sample_user) + new_remaining = 2*settings.INVITATIONS_PER_USER + 1 + invitation_user.invitations_remaining = new_remaining + invitation_user.save() + remaining = remaining_invites(self.sample_user) + self.assertEqual(remaining, new_remaining) + + class InvitationFormTests(InvitationTestCase): """ Tests for the forms and custom validation logic included in