From: Ray Speth Date: Tue, 16 Mar 2010 01:23:42 +0000 (-0400) Subject: Allow Splits to be mathematical expressions X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=f03d48ccc07789e1c56b07cec50820f3ad26f8ba;hp=7f12cce3f2b932dedab7d80f03aa063ab1d07523;p=bluechips.git Allow Splits to be mathematical expressions The string representing the split is stored in the DB, along with its decimal equivalent. When viewing an expense, the string form is shown. --- diff --git a/bluechips/controllers/spend.py b/bluechips/controllers/spend.py index cf691cc..d27ec40 100644 --- a/bluechips/controllers/spend.py +++ b/bluechips/controllers/spend.py @@ -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() diff --git a/bluechips/model/__init__.py b/bluechips/model/__init__.py index bb3b2d9..90c8b4d 100644 --- a/bluechips/model/__init__.py +++ b/bluechips/model/__init__.py @@ -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) ) diff --git a/bluechips/model/expenditure.py b/bluechips/model/expenditure.py index 4669ad1..8fd706b 100644 --- a/bluechips/model/expenditure.py +++ b/bluechips/model/expenditure.py @@ -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): diff --git a/bluechips/model/split.py b/bluechips/model/split.py index 8bf3b2e..3ee1f17 100644 --- a/bluechips/model/split.py +++ b/bluechips/model/split.py @@ -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 '' % (self.expenditure, self.user,