+from bluechips.model.user import User
+from bluechips.model.split import Split
+from bluechips.model import meta
+from bluechips.model.types import Currency
+from decimal import Decimal
+from datetime import datetime
+import random
+
class Expenditure(object):
+ def __init__(self, spender=None, amount=Currency(0), description=u"",
+ date=None):
+ self.spender = spender
+ self.amount = amount
+ self.description = description
+ if self.date == None:
+ self.date = datetime.now()
+
def __repr__(self):
return '<Expenditure: spender: %s spent: %s>' % (self.spender,
self.amount)
+ def even_split(self):
+ """
+ Split up an expenditure evenly among the resident users
+ """
+
+ residents = meta.Session.query(User).filter(User.resident==True)
+ split_percentage = Decimal(100) / Decimal(residents.count())
+ self.split(dict((resident, split_percentage) for resident in residents))
+
+ def split(self, split_dict):
+ """
+ Split up an expenditure.
+
+ split_dict should be a dict mapping from bluechips.model:User
+ objects to a decimal:Decimal object representing the percentage
+ that user is responsible for.
+
+ Percentages will be normalized to sum to 100%.
+
+ If the split leaks or gains money due to rounding errors, the
+ pennies will be randomly distributed to one of the users.
+
+ I mean, come on. You're already living together. Are you really
+ going to squabble over a few pennies?
+ """
+
+ map(meta.Session.delete, meta.Session.query(Split).\
+ filter_by(expenditure_id=self.id))
+
+ total = sum(split_dict.itervalues())
+
+ for user, share in split_dict.items():
+ if share == 0:
+ del split_dict[user]
+
+ amounts_dict = dict()
+
+ for user, share in split_dict.iteritems():
+ amounts_dict[user] = Currency((share * self.amount) / total)
+
+ difference = self.amount - sum(amounts_dict.itervalues())
+
+ if difference > 0:
+ for i in xrange(difference):
+ winner = random.choice(amounts_dict.keys())
+ amounts_dict[winner] += Currency(1)
+ elif difference < 0:
+ for i in xrange(-difference):
+ winner = random.choice(amounts_dict.keys())
+ amounts_dict[winner] -= Currency(1)
+
+ for user, share in amounts_dict.iteritems():
+ s = Split(self, user, share)
+ meta.Session.add(s)
+
+ def involves(self, user):
+ "Returns True if ``user`` is involved in this expenditure."
+ return (any((split.user == user) and (split.share != 0)
+ for split in self.splits) or
+ (self.spender == user))
+
+ def share(self, user):
+ "Return the share corresponding to ``user``."
+ shares = dict((split.user, split.share)
+ for split in self.splits)
+ return shares.get(user, Currency(0))
+
__all__ = ['Expenditure']