]> asedeno.scripts.mit.edu Git - bluechips.git/commitdiff
Allow Splits to be mathematical expressions
authorRay Speth <speth@mit.edu>
Tue, 16 Mar 2010 01:23:42 +0000 (21:23 -0400)
committerAlejandro R. Sedeño <asedeno@mit.edu>
Wed, 23 Feb 2011 02:39:20 +0000 (21:39 -0500)
The string representing the split is stored in the DB, along with its
decimal equivalent. When viewing an expense, the string form is shown.

bluechips/controllers/spend.py
bluechips/model/__init__.py
bluechips/model/expenditure.py
bluechips/model/split.py

index cf691cce522cf74b6eb1ea4c498584c1cf3de868..d27ec40d2dc448507b8d50ef1c83a9bddaf61867 100644 (file)
@@ -2,8 +2,10 @@
 Handle expenditures
 """
 
+from __future__ import division
 import logging
 
+import re
 from decimal import Decimal, InvalidOperation
 
 from bluechips.lib.base import *
@@ -13,6 +15,7 @@ from pylons.decorators import validate
 from pylons.decorators.secure import authenticate_form
 from pylons.controllers.util import abort
 
+import formencode
 from formencode import validators, Schema
 from formencode.foreach import ForEach
 from formencode.variabledecode import NestedVariables
@@ -22,12 +25,29 @@ from mailer import Message
 
 log = logging.getLogger(__name__)
 
+class ExpenditureExpression(validators.FancyValidator):
+    goodChars = set('1234567890.+-/*() ')
+
+    def _to_python(self, value, state):
+        if (not set(value) <= self.goodChars or
+            re.search(r'([\+\-\*\/])\1', value)):
+            raise formencode.Invalid("Expression contains illegal characters", value, state)
+
+        if value == '':
+            return value, Decimal("0")
+
+        try:
+            number = eval(value)
+            return value, Decimal(str(number))
+        except:
+            raise formencode.Invalid("Not a valid mathematical expression", value, state)
+
 
 class ShareSchema(Schema):
     "Validate individual user shares."
     allow_extra_fields = False
     user_id = validators.Int(not_empty=True)
-    amount = validators.Number(not_empty=True)
+    amount = ExpenditureExpression()
 
 
 def validate_state(value_dict, state, validator):
@@ -77,16 +97,10 @@ class SpendController(BaseController):
             c.values = {}
             for ii, user_row in enumerate(c.users):
                 user_id, user = user_row
-                shares_by_user = dict(((sp.user, sp.share) for sp
+                shares_by_user = dict(((sp.user, sp.share_text) for sp
                                        in c.expenditure.splits))
-                share = shares_by_user.get(user, 0)
-                if c.expenditure.amount == 0:
-                    percent = 0
-                else:
-                    percent = (Decimal(100) * Decimal(int(share)) /
-                               Decimal(int(c.expenditure.amount))).\
-                            quantize(Decimal("0.001"))
-                c.values['shares-%d.amount' % ii] = percent
+                share = shares_by_user.get(user, '')
+                c.values['shares-%d.amount' % ii] = share
 
         return render('/spend/index.mako')
 
@@ -112,10 +126,13 @@ class SpendController(BaseController):
 
         users = dict(meta.Session.query(model.User.id, model.User).all())
         split_dict = {}
+        split_text_dict = {}
         for share_params in shares:
             user = users[share_params['user_id']]
-            split_dict[user] = Decimal(str(share_params['amount']))
-        e.split(split_dict)
+            amount_text, amount  = share_params['amount'] or ('',Decimal('0'))
+            split_dict[user] = amount
+            split_text_dict[user] = amount_text
+        e.split(split_dict, split_text_dict)
         
         meta.Session.commit()
        
index bb3b2d9caa4689b390e1a5a05891f4afaf9e83f7..90c8b4d559f28e931a5b67056142e2daea321d22 100644 (file)
@@ -49,6 +49,7 @@ splits = sa.Table('splits', meta.metadata,
                             sa.ForeignKey('expenditures.id'), nullable=False),
                   sa.Column('user_id', sa.types.Integer,
                             sa.ForeignKey('users.id'), nullable=False),
+                  sa.Column('share_text', sa.Text, nullable=False),
                   sa.Column('share', types.DBCurrency, nullable=False)
                   )
 
index 4669ad1090c740aaac7284c1903bd01cbac0d037..8fd706bfd5c358046c66ec2e68d80852c1365fb2 100644 (file)
@@ -28,7 +28,7 @@ class Expenditure(object):
         split_percentage = Decimal(100) / Decimal(residents.count())
         self.split(dict((resident, split_percentage) for resident in residents))
     
-    def split(self, split_dict):
+    def split(self, split_dict, split_text_dict):
         """
         Split up an expenditure.
         
@@ -71,7 +71,7 @@ class Expenditure(object):
                 amounts_dict[winner] -= Currency(1)
         
         for user, share in amounts_dict.iteritems():
-            s = Split(self, user, share)
+            s = Split(self, user, share, split_text_dict[user])
             meta.Session.add(s)
 
     def involves(self, user):
index 8bf3b2e791a31a529c492e0e3c280417842cd315..3ee1f1754da2241acb2827f72e692f8762206a75 100644 (file)
@@ -1,11 +1,12 @@
 from types import Currency
 
 class Split(object):
-    def __init__(self, expenditure=None, user=None, share=Currency(0)):
+    def __init__(self, expenditure=None, user=None, share=Currency(0), share_text=u""):
         self.expenditure = expenditure
         self.user = user
         self.share = share
-    
+        self.share_text = share_text
+
     def __repr__(self):
         return '<Split: expense: %s user: %s share: %s>' % (self.expenditure,
                                                             self.user,