From f03d48ccc07789e1c56b07cec50820f3ad26f8ba Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Mon, 15 Mar 2010 21:23:42 -0400 Subject: [PATCH] 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. --- bluechips/controllers/spend.py | 41 ++++++++++++++++++++++++---------- bluechips/model/__init__.py | 1 + bluechips/model/expenditure.py | 4 ++-- bluechips/model/split.py | 5 +++-- 4 files changed, 35 insertions(+), 16 deletions(-) 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, -- 2.45.2