]> asedeno.scripts.mit.edu Git - bluechips.git/blobdiff - bluechips/controllers/spend.py
Added UI for working with tags
[bluechips.git] / bluechips / controllers / spend.py
index cf691cce522cf74b6eb1ea4c498584c1cf3de868..43293071bdca31592c60f10d575bd281a6872363 100644 (file)
@@ -2,8 +2,11 @@
 Handle expenditures
 """
 
+from __future__ import division
 import logging
 
+import re
+import string
 from decimal import Decimal, InvalidOperation
 
 from bluechips.lib.base import *
@@ -13,6 +16,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 +26,35 @@ 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 TagValidator(validators.FancyValidator):
+    def _to_python(self, value,state):
+        try:
+            return set(map(string.strip, value.split(',')))
+        except:
+            raise formencode.Invalid("Unable to parse tags", 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):
@@ -36,6 +63,12 @@ def validate_state(value_dict, state, validator):
 ValidateNotAllZero = SimpleFormValidator(validate_state)
 
 
+def prune_tags():
+    for tag in meta.Session.query(model.Tag).all():
+        if not tag.expenditures:
+            meta.Session.delete(tag)
+    meta.Session.commit()
+
 class ExpenditureSchema(Schema):
     "Validate an expenditure."
     allow_extra_fields = False
@@ -43,6 +76,7 @@ class ExpenditureSchema(Schema):
     spender_id = validators.Int(not_empty=True)
     amount = model.types.CurrencyValidator(not_empty=True)
     description = validators.UnicodeString(not_empty=True)
+    tags = TagValidator()
     date = validators.DateConverter()
     shares = ForEach(ShareSchema)
     chained_validators = [ValidateNotAllZero]
@@ -67,8 +101,10 @@ class SpendController(BaseController):
                 user_id, user = user_row
                 val = 0
                 if user.resident:
-                    val = Decimal(100) / Decimal(num_residents)
+                    val = Decimal(1)
                 c.values['shares-%d.amount' % ii] = val
+
+            c.values['tags'] = u""
         else:
             c.title = 'Edit an Expenditure'
             c.expenditure = meta.Session.query(model.Expenditure).get(id)
@@ -77,16 +113,12 @@ 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
+
+            c.values['tags'] = ', '.join(c.expenditure.tags)
 
         return render('/spend/index.mako')
 
@@ -108,15 +140,21 @@ class SpendController(BaseController):
         
         # Set the fields that were submitted
         shares = self.form_result.pop('shares')
+        tags = self.form_result.pop('tags') or set()
         update_sar(e, self.form_result)
 
         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)
+        e.tags.clear()
+        e.tags |= tags
+
         meta.Session.commit()
        
         show = ("Expenditure of %s paid for by %s %s." %
@@ -131,6 +169,8 @@ class SpendController(BaseController):
                                   'op': op})
         g.handle_notification(involved_users, show, body)
 
+        prune_tags()
+
         return h.redirect_to('/')
 
     def delete(self, id):
@@ -163,4 +203,6 @@ class SpendController(BaseController):
                                       'op': 'deleted'})
             g.handle_notification(involved_users, show, body)
 
+            prune_tags()
+
         return h.redirect_to('/')