]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/model/expenditure.py
don't need update_split() anymore
[bluechips.git] / bluechips / model / expenditure.py
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
7 import random
8
9 class Expenditure(object):
10     def __init__(self, spender=None, amount=Currency(0), description=u"",
11                  date=None):
12         self.spender = spender
13         self.amount = amount
14         self.description = description
15         if self.date == None:
16             self.date = datetime.now()
17     
18     def __repr__(self):
19         return '<Expenditure: spender: %s spent: %s>' % (self.spender,
20                                                          self.amount)
21
22     def even_split(self):
23         """
24         Split up an expenditure evenly among the resident users
25         """
26         
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))
30     
31     def split(self, split_dict):
32         """
33         Split up an expenditure.
34         
35         split_dict should be a dict mapping from bluechips.model:User
36         objects to a decimal:Decimal object representing the percentage
37         that user is responsible for.
38         
39         Percentages will be normalized to sum to 100%.
40         
41         If the split leaks or gains money due to rounding errors, the
42         pennies will be randomly distributed to one of the users.
43         
44         I mean, come on. You're already living together. Are you really
45         going to squabble over a few pennies?
46         """
47         
48         map(meta.Session.delete, meta.Session.query(Split).\
49                 filter_by(expenditure_id=self.id))
50         
51         total = sum(split_dict.itervalues())
52         
53         for user, share in split_dict.items():
54             if share == 0:
55                 del split_dict[user]
56             else:
57                 split_dict[user] = share / total
58             
59         amounts_dict = dict()
60         
61         for user, share in split_dict.iteritems():
62             amounts_dict[user] = Currency(split_dict[user] * self.amount)
63         
64         difference = self.amount - sum(amounts_dict.itervalues())
65         
66         if difference > 0:
67             for i in xrange(difference):
68                 winner = random.choice(amounts_dict.keys())
69                 amounts_dict[winner] += Currency(1)
70         elif difference < 0:
71             for i in xrange(-difference):
72                 winner = random.choice(amounts_dict.keys())
73                 amounts_dict[winner] -= Currency(1)
74         
75         for user, share in amounts_dict.iteritems():
76             s = Split(self, user, share)
77             meta.Session.add(s)
78
79     def involves(self, user):
80         "Returns True if ``user`` is involved in this expenditure."
81         return (any((split.user == user) and (split.share != 0)
82                     for split in self.splits) or
83                 (self.spender == user))
84
85     def share(self, user):
86         "Return the share corresponding to ``user``."
87         shares = dict((split.user, split.share)
88                       for split in self.splits)
89         return shares.get(user, Currency(0))
90
91 __all__ = ['Expenditure']