]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/controllers/spend.py
Merge remote branch 'storborg/master'
[bluechips.git] / bluechips / controllers / spend.py
1 """
2 Handle expenditures
3 """
4
5 import logging
6
7 from decimal import Decimal, InvalidOperation
8
9 from bluechips.lib.base import *
10
11 from pylons import request, app_globals as g
12 from pylons.decorators.rest import dispatch_on
13 from pylons.decorators import validate
14 from pylons.controllers.util import abort
15
16 from formencode import validators, Schema
17 from formencode.foreach import ForEach
18 from formencode.variabledecode import NestedVariables
19 from formencode.schema import SimpleFormValidator
20
21 from mailer import Message
22
23 log = logging.getLogger(__name__)
24
25
26 class ShareSchema(Schema):
27     "Validate individual user shares."
28     allow_extra_fields = False
29     user_id = validators.Int(not_empty=True)
30     amount = validators.Number(not_empty=True)
31
32
33 def validate_state(value_dict, state, validator):
34     if all(s['amount'] == 0 for s in value_dict['shares']):
35         return {'shares-0.amount': 'Need at least one non-zero share'}
36 ValidateNotAllZero = SimpleFormValidator(validate_state)
37
38
39 class ExpenditureSchema(Schema):
40     "Validate an expenditure."
41     allow_extra_fields = False
42     pre_validators = [NestedVariables()]
43     spender_id = validators.Int(not_empty=True)
44     amount = model.types.CurrencyValidator(not_empty=True)
45     description = validators.UnicodeString()
46     date = validators.DateConverter()
47     shares = ForEach(ShareSchema)
48     chained_validators = [ValidateNotAllZero]
49     
50
51 class SpendController(BaseController):
52     def index(self):
53         return self.edit()
54     
55     def edit(self, id=None):
56         c.users = meta.Session.query(model.User.id, model.User)
57         if id is None:
58             c.title = 'Add a New Expenditure'
59             c.expenditure = model.Expenditure()
60             c.expenditure.spender_id = request.environ['user'].id
61
62             num_residents = meta.Session.query(model.User).\
63                     filter_by(resident=True).count()
64             # Pre-populate split percentages for an even split.
65             c.values = {}
66             for ii, user_row in enumerate(c.users):
67                 user_id, user = user_row
68                 val = 0
69                 if user.resident:
70                     val = Decimal(100) / Decimal(num_residents)
71                 c.values['shares-%d.amount' % ii] = val
72         else:
73             c.title = 'Edit an Expenditure'
74             c.expenditure = meta.Session.query(model.Expenditure).get(id)
75             if c.expenditure is None:
76                 abort(404)
77             c.values = {}
78             for ii, user_row in enumerate(c.users):
79                 user_id, user = user_row
80                 shares_by_user = dict(((sp.user, sp.share) for sp
81                                        in c.expenditure.splits))
82                 share = shares_by_user.get(user, 0)
83                 if c.expenditure.amount == 0:
84                     percent = 0
85                 else:
86                     percent = (Decimal(100) * Decimal(int(share)) /
87                                Decimal(int(c.expenditure.amount))).\
88                             quantize(Decimal("0.001"))
89                 c.values['shares-%d.amount' % ii] = percent
90
91         return render('/spend/index.mako')
92
93     @redirect_on_get('edit')
94     @validate(schema=ExpenditureSchema(), form='edit', variable_decode=True)
95     def update(self, id=None):
96         # Either create a new object, or, if we're editing, get the
97         # old one
98         if id is None:
99             e = model.Expenditure()
100             meta.Session.add(e)
101             op = 'created'
102         else:
103             e = meta.Session.query(model.Expenditure).get(id)
104             if e is None:
105                 abort(404)
106             op = 'updated'
107         
108         # Set the fields that were submitted
109         shares = self.form_result.pop('shares')
110         update_sar(e, self.form_result)
111
112         users = dict(meta.Session.query(model.User.id, model.User).all())
113         split_dict = {}
114         for share_params in shares:
115             user = users[share_params['user_id']]
116             split_dict[user] = Decimal(str(share_params['amount']))
117         e.split(split_dict)
118         
119         meta.Session.commit()
120        
121         show = ("Expenditure of %s paid for by %s %s." %
122                 (e.amount, e.spender, op))
123         h.flash(show)
124
125         # Send email notification to involved users if they have an email set.
126         involved_users = set(sp.user for sp in e.splits if sp.share != 0)
127         involved_users.add(e.spender)
128         body = render('/emails/expenditure.txt',
129                       extra_vars={'expenditure': e,
130                                   'op': op})
131         g.handle_notification(involved_users, show, body)
132
133         return h.redirect_to('/')