1 from bluechips.model.user import User
2 from bluechips.model.split import Split
3 from bluechips.model import meta
4 from bluechips.model.types import Currency
5 from decimal import Decimal
6 from datetime import datetime
9 class Expenditure(object):
10 def __init__(self, spender=None, amount=Currency(0), description=u"",
12 self.spender = spender
14 self.description = description
16 self.date = datetime.now()
19 return '<Expenditure: spender: %s spent: %s>' % (self.spender,
24 Split up an expenditure evenly among the resident users
27 residents = meta.Session.query(User).filter(User.resident==True)
28 split_percentage = Decimal(100) / Decimal(residents.count())
29 self.split(dict((resident, split_percentage) for resident in residents))
31 def update_split(self):
33 Re-split an expenditure using the same percentages as what is
34 currently in the database
37 old_splits = meta.Session.query(Split).filter(Split.expenditure==self)
38 split_dict = dict((s.user, Decimal(int(s.share))) for s in old_splits)
39 self.split(split_dict)
41 def split(self, split_dict):
43 Split up an expenditure.
45 split_dict should be a dict mapping from bluechips.model:User
46 objects to a decimal:Decimal object representing the percentage
47 that user is responsible for.
49 Percentages will be normalized to sum to 100%.
51 If the split leaks or gains money due to rounding errors, the
52 pennies will be randomly distributed to one of the users.
54 I mean, come on. You're already living together. Are you really
55 going to squabble over a few pennies?
58 map(meta.Session.delete, meta.Session.query(Split).\
59 filter_by(expenditure_id=self.id))
61 total = sum(split_dict.itervalues())
63 for user, share in split_dict.items():
67 split_dict[user] = share / total
71 for user, share in split_dict.iteritems():
72 amounts_dict[user] = Currency(split_dict[user] * self.amount)
74 difference = self.amount - sum(amounts_dict.itervalues())
77 for i in xrange(difference):
78 winner = random.choice(amounts_dict.keys())
79 amounts_dict[winner] += Currency(1)
81 for i in xrange(-difference):
82 winner = random.choice(amounts_dict.keys())
83 amounts_dict[winner] -= Currency(1)
85 for user, share in amounts_dict.iteritems():
86 s = Split(self, user, share)
89 def involves(self, user):
90 "Returns True if ``user`` is involved in this expenditure."
91 return (any((split.user == user) and (split.share != 0)
92 for split in self.splits) or
93 (self.spender == user))
95 def share(self, user):
96 "Return the share corresponding to ``user``."
97 shares = dict((split.user, split.share)
98 for split in self.splits)
99 return shares.get(user, Currency(0))
101 __all__ = ['Expenditure']