Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
implement membership
Browse files Browse the repository at this point in the history
  • Loading branch information
chadwhitacre committed May 11, 2016
1 parent 0371cf2 commit af9aeae
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 2 deletions.
2 changes: 1 addition & 1 deletion gratipay/models/team/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def slugize(name):
return slug


class Team(Model, mixins.Takes):
class Team(Model, mixins.Takes, mixins.Membership):
"""Represent a Gratipay team.
"""

Expand Down
3 changes: 2 additions & 1 deletion gratipay/models/team/mixins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .membership import MembershipMixin as Membership
from .takes import TakesMixin as Takes

__all__ = ['Takes']
__all__ = ['Membership', 'Takes']
55 changes: 55 additions & 0 deletions gratipay/models/team/mixins/membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from __future__ import absolute_import, division, print_function, unicode_literals


class NoRoom(Exception):
pass


class MembershipMixin(object):
"""This mixin provides membership management for
:py:class:`~gratipay.models.team.Team` objects. It depends on API in the
:py:class:`~gratipay.models.team.mixins.Takes` mixin.
"""

@property
def nmembers(self):
return self.ndistributing_to


def get_memberships(self, cursor=None):
"""Return a list of memberships for this team.
"""
return (cursor or self.db).all("""
SELECT cm.*
, (SELECT p.*::participants
FROM participants p
WHERE p.id=participant_id) AS participant
FROM memberships cm
JOIN teams t
ON t.id = cm.team_id
WHERE t.id = %s
AND t.ntakes > 0
""", (self.id,))


def add_member(self, participant):
"""Add a participant to this team.
:param Participant participant: the participant to add
:raises NoRoom: if are no unclaimed takes for the participant to claim
"""
ntakes = self.set_ntakes_for(participant, 1)
if ntakes == 0:
raise NoRoom


def remove_member(self, participant):
"""Remove a participant from this team.
:param Participant participant: the participant to remove
"""
self.set_ntakes_for(participant, 0)
73 changes: 73 additions & 0 deletions tests/py/test_team_membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from test_team_takes import TeamTakesHarness
from gratipay.models.team import mixins


class Tests(TeamTakesHarness):

def assert_memberships(self, *expected):
actual = self.enterprise.get_memberships()
assert [(m.participant.username, m.ntakes) for m in actual] == list(expected)


def test_team_object_subclasses_takes_mixin(self):
assert isinstance(self.enterprise, mixins.Membership)


# gm - get_memberships

def test_gm_returns_an_empty_list_when_there_are_no_members(self):
assert self.enterprise.get_memberships() == []

def test_gm_returns_memberships_when_there_are_members(self):
self.enterprise.add_member(self.crusher)
assert len(self.enterprise.get_memberships()) == 1

def test_gm_returns_more_memberships_when_there_are_more_members(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
assert len(self.enterprise.get_memberships()) == 2


# am - add_member

def test_am_adds_a_member(self):
self.enterprise.add_member(self.crusher)
self.assert_memberships(('crusher', 1))

def test_am_adds_another_member(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
self.assert_memberships(('crusher', 1), ('bruiser', 1))

def test_am_affects_cacheroonies_as_expected(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
assert self.enterprise.nmembers == 2
assert self.enterprise.ntakes_claimed == 2
assert self.enterprise.ntakes_unclaimed == 998


# rm - remove_member

def test_rm_removes_a_member(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
self.enterprise.remove_member(self.crusher)
self.assert_memberships(('bruiser', 1))

def test_rm_removes_another_member(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
self.enterprise.remove_member(self.crusher)
self.enterprise.remove_member(self.bruiser)
self.assert_memberships()

def test_rm_affects_cacheroonies_as_expected(self):
self.enterprise.add_member(self.crusher)
self.enterprise.add_member(self.bruiser)
self.enterprise.remove_member(self.crusher)
assert self.enterprise.nmembers == 1
assert self.enterprise.ntakes_claimed == 1
assert self.enterprise.ntakes_unclaimed == 999

0 comments on commit af9aeae

Please sign in to comment.