]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/model/expenditure.py
Added UI for working with tags
[bluechips.git] / bluechips / model / expenditure.py
1 from sqlalchemy.ext.associationproxy import association_proxy
2
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
10 import random
11
12 class Expenditure(object):
13     def __init__(self, spender=None, amount=Currency(0), description=u"",
14                  date=None):
15         self.spender = spender
16         self.amount = amount
17         self.description = description
18         if self.date == None:
19             self.date = datetime.now()
20     
21     tags = association_proxy('_tags', 'name', creator=create_tag)
22
23     def __repr__(self):
24         return '<Expenditure: spender: %s spent: %s>' % (self.spender,
25                                                          self.amount)
26
27     def even_split(self):
28         """
29         Split up an expenditure evenly among the resident users
30         """
31         
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))
35     
36     def split(self, split_dict, split_text_dict):
37         """
38         Split up an expenditure.
39         
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.
43         
44         Percentages will be normalized to sum to 100%.
45         
46         If the split leaks or gains money due to rounding errors, the
47         pennies will be randomly distributed to one of the users.
48         
49         I mean, come on. You're already living together. Are you really
50         going to squabble over a few pennies?
51         """
52         
53         map(meta.Session.delete, meta.Session.query(Split).\
54                 filter_by(expenditure_id=self.id))
55         
56         total = sum(split_dict.itervalues())
57         
58         for user, share in split_dict.items():
59             if share == 0:
60                 del split_dict[user]
61             
62         amounts_dict = dict()
63         
64         for user, share in split_dict.iteritems():
65             amounts_dict[user] = Currency((share * self.amount) / total)
66         
67         difference = self.amount - sum(amounts_dict.itervalues())
68         
69         if difference > 0:
70             for i in xrange(difference):
71                 winner = random.choice(amounts_dict.keys())
72                 amounts_dict[winner] += Currency(1)
73         elif difference < 0:
74             for i in xrange(-difference):
75                 winner = random.choice(amounts_dict.keys())
76                 amounts_dict[winner] -= Currency(1)
77         
78         for user, share in amounts_dict.iteritems():
79             s = Split(self, user, share, split_text_dict[user])
80             meta.Session.add(s)
81
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))
87
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))
93
94 __all__ = ['Expenditure']