1 from sqlalchemy.ext.associationproxy import association_proxy
3 from bluechips.model.user import User
4 from bluechips.model.split import Split
5 from bluechips.model import meta
6 from bluechips.model.types import Currency
7 from bluechips.model.tag import create_tag
8 from decimal import Decimal
9 from datetime import datetime
12 class Expenditure(object):
13 def __init__(self, spender=None, amount=Currency(0), description=u"",
15 self.spender = spender
17 self.description = description
19 self.date = datetime.now()
21 tags = association_proxy('_tags', 'name', creator=create_tag)
24 return '<Expenditure: spender: %s spent: %s>' % (self.spender,
29 Split up an expenditure evenly among the resident users
32 residents = meta.Session.query(User).filter(User.resident==True)
33 split_percentage = Decimal(100) / Decimal(residents.count())
34 self.split(dict((resident, split_percentage) for resident in residents))
36 def split(self, split_dict, split_text_dict):
38 Split up an expenditure.
40 split_dict should be a dict mapping from bluechips.model:User
41 objects to a decimal:Decimal object representing the percentage
42 that user is responsible for.
44 Percentages will be normalized to sum to 100%.
46 If the split leaks or gains money due to rounding errors, the
47 pennies will be randomly distributed to one of the users.
49 I mean, come on. You're already living together. Are you really
50 going to squabble over a few pennies?
53 map(meta.Session.delete, meta.Session.query(Split).\
54 filter_by(expenditure_id=self.id))
56 total = sum(split_dict.itervalues())
58 for user, share in split_dict.items():
64 for user, share in split_dict.iteritems():
65 amounts_dict[user] = Currency((share * self.amount) / total)
67 difference = self.amount - sum(amounts_dict.itervalues())
70 for i in xrange(difference):
71 winner = random.choice(amounts_dict.keys())
72 amounts_dict[winner] += Currency(1)
74 for i in xrange(-difference):
75 winner = random.choice(amounts_dict.keys())
76 amounts_dict[winner] -= Currency(1)
78 for user, share in amounts_dict.iteritems():
79 s = Split(self, user, share, split_text_dict[user])
82 def involves(self, user):
83 "Returns True if ``user`` is involved in this expenditure."
84 return (any((split.user == user) and (split.share != 0)
85 for split in self.splits) or
86 (self.spender == user))
88 def share(self, user):
89 "Return the share corresponding to ``user``."
90 shares = dict((split.user, split.share)
91 for split in self.splits)
92 return shares.get(user, Currency(0))
94 __all__ = ['Expenditure']