]> asedeno.scripts.mit.edu Git - bluechips.git/blobdiff - bluechips/model/expenditure.py
Expenditures can now be given a list of "tags"
[bluechips.git] / bluechips / model / expenditure.py
index c093e75cdf3f8219120d4af080c3e7fc13369a84..6b3b1cb242c6c1e4ecd4de1fef70e537870cc3b6 100644 (file)
@@ -1,6 +1,98 @@
+from bluechips.model.user import User
+from bluechips.model.split import Split
+from bluechips.model import meta
+from bluechips.model.types import Currency
+from bluechips.model.tag import Tag
+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_text_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, split_text_dict[user])
+            meta.Session.add(s)
+
+    def tag(self, tags):
+        map(meta.Session.delete,
+            meta.Session.query(Tag).filter_by(expenditure_id=self.id))
+
+        for tag in tags:
+            t = Tag(self, tag)
+            meta.Session.add(t)
+
+    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']