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 Jul 1, 2016
1 parent f6429c0 commit 91a9ca1
Show file tree
Hide file tree
Showing 4 changed files with 129 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 @@ -33,7 +33,7 @@ def slugize(name):
return slug


class Team(Model, mixins.Takes, mixins.TipMigration):
class Team(Model, mixins.Takes, mixins.TipMigration, 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,4 +1,5 @@
from .membership import MembershipMixin as Membership
from .takes import TakesMixin as Takes
from .tip_migration import TipMigrationMixin as TipMigration

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

from gratipay.models.team.mixins.takes import ZERO, PENNY


class MembershipMixin(object):
"""Teams may have zero or more members.
"""


@property
def nmembers(self):
"""The number of members. Read-only and computed (not in the db); equal to
:py:attr:`~gratipay.models.team.mixins.takes.ndistributing_to`.
"""
return self.ndistributing_to


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


def add_member(self, participant, recorder):
"""Add a participant to this team.
:param Participant participant: the participant to add
:param Participant recorder: the participant making the change
"""
self.set_take_for(participant, PENNY, recorder)


def remove_member(self, participant, recorder):
"""Remove a participant from this team.
:param Participant participant: the participant to remove
:param Participant recorder: the participant making the change
"""
self.set_take_for(participant, ZERO, recorder)
72 changes: 72 additions & 0 deletions tests/py/test_team_membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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 setUp(self):
TeamTakesHarness.setUp(self)

def assert_memberships(self, *expected):
actual = self.enterprise.get_memberships()
assert [m.participant.username 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, self.picard)
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.picard)
self.enterprise.add_member(self.bruiser, self.picard)
assert len(self.enterprise.get_memberships()) == 2


# am - add_member

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

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

def test_am_affects_computed_values_as_expected(self):
self.enterprise.add_member(self.crusher, self.picard)
self.enterprise.add_member(self.bruiser, self.picard)
assert self.enterprise.nmembers == 2


# rm - remove_member

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

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

def test_rm_affects_computed_values_as_expected(self):
self.enterprise.add_member(self.crusher, self.picard)
self.enterprise.add_member(self.bruiser, self.picard)
self.enterprise.remove_member(self.crusher, self.crusher)
assert self.enterprise.nmembers == 1

0 comments on commit 91a9ca1

Please sign in to comment.