# any Pylons config options)
config['pylons.app_globals'].mailer = Mailer(config.get('mailer.host',
'127.0.0.1'))
- if 'mailer.user' in config:
- config['pylons.app_globals'].mailer.login(config['mailer.user'],
- config['mailer.password'])
c.values = {}
for ii, user_row in enumerate(c.users):
user_id, user = user_row
+ val = 0
if user.resident:
val = Decimal(100) / Decimal(num_residents)
- else:
- val = 0
c.values['shares-%d.amount' % ii] = val
else:
c.title = 'Edit an Expenditure'
c.values = {}
for ii, user_row in enumerate(c.users):
user_id, user = user_row
- try:
- share = [s.share for s in c.expenditure.splits
- if s.user == 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"))
- except IndexError:
+ shares_by_user = dict(((sp.user, sp.share) 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
return render('/spend/index.mako')
class UserController(BaseController):
def index(self):
+ c.title = 'User Settings'
return render('/user/index.mako')
@validate(schema=EmailSchema(), form='index')
def update(self):
new_email = self.form_result['new_email']
- if new_email == '':
- new_email = None
request.environ['user'].email = new_email
meta.Session.commit()
if new_email is None:
log.info("From: %s\nTo: %s\nSubject: %s\n\n%s",
msg.From, msg.To, msg.Subject, msg.Body)
else:
- self.mailer.send(msg)
+ self.mailer.send(msg) # pragma: nocover
def handle_notification(self, users, subject, body):
"Send a notification email."
class BlueChipUser(RequestPermission):
def check(self, app, environ, start_response):
if 'REMOTE_USER' not in environ:
- raise NotAuthenticatedError('Not Authenticated')
+ raise NotAuthenticatedError('Not Authenticated') # pragma: nocover
environ['user'] = meta.Session.query(model.User).\
filter_by(username=unicode(environ['REMOTE_USER'])).\
first()
if environ['user'] == None:
- raise NotAuthorizedError('You are not allowed access.')
+ raise NotAuthorizedError('You are not allowed access.') # pragma: nocover
return app(environ, start_response)
class DummyAuthenticate(AddDictToEnviron):
settle_list.append((owes['who'], owed['who'], val))
if len(owes_list) > 0:
- raise DirtyBooks, ("People still owe money", owes_list)
+ raise DirtyBooks, ("People still owe money", owes_list) #pragma:nocover
if len(owed_list) > 0:
- raise DirtyBooks, ("People are still owed money", owed_list)
+ raise DirtyBooks, ("People are still owed money", owed_list) #pragma:nocover
return settle_list
split_percentage = Decimal(100) / Decimal(residents.count())
self.split(dict((resident, split_percentage) for resident in residents))
- def update_split(self):
- """
- Re-split an expenditure using the same percentages as what is
- currently in the database
- """
-
- old_splits = meta.Session.query(Split).filter(Split.expenditure==self)
- split_dict = dict((s.user, Decimal(int(s.share))) for s in old_splits)
- self.split(split_dict)
-
def split(self, split_dict):
"""
Split up an expenditure.
class Subitem(object):
def __init__(self, expenditure=None, user=None, amount=Currency(0)):
- self.expenditure = expenditure
- self.user = user
- self.share = share
+ self.expenditure = expenditure # pragma: nocover
+ self.user = user # pragma: nocover
+ self.share = share # pragma: nocover
def __repr__(self):
- return '<Subitem: expense: %s user: %s cost: %s>' % (self.expense,
- self.user,
- self.amount)
+ return ('<Subitem: expense: %s user: %s cost: %s>' %
+ (self.expense, self.user, self.amount)) # pragma: nocover
__all__ = ['Subitem']
"""
return self.__mul__(other)
- def __str_no_dollar__(self):
- """
- Get to the formatted string without the dollar sign
- """
- return str(self).replace('$', '')
-
def __repr__(self):
return '%s("%s")' % (self.__class__.__name__, str(self))
def __str__(self):
# Invoke websetup with the current config file
SetupCommand('setup-app').run([config['__file__']])
- meta.Session.add(bluechips.model.User(u'root', u'Charlie Root', True))
- meta.Session.add(bluechips.model.User(u'ben', u'Ben Bitdiddle', True))
- meta.Session.add(bluechips.model.User(u'gotta', u'Gotta Lisp', True))
- meta.Session.add(bluechips.model.User(u'rich', u'Rich Scheme', True))
+ u1 = bluechips.model.User(u'root', u'Charlie Root', True)
+ u1.email = u'charlie@example.com'
+ u2 = bluechips.model.User(u'ben', u'Ben Bitdiddle', True)
+ u3 = bluechips.model.User(u'gotta', u'Gotta Lisp', True)
+ u4 = bluechips.model.User(u'rich', u'Rich Scheme', True)
+ for u in (u1, u2, u3, u4):
+ meta.Session.add(u)
meta.Session.commit()
def tearDownPackage():
from datetime import date
+from formencode import Invalid
+
from bluechips.tests import *
from bluechips import model
from bluechips.model import meta
+from bluechips.model.types import Currency
+
+from bluechips.controllers.spend import ExpenditureSchema
class TestSpendController(TestController):
filter_by(name=u'Charlie Root').one()
form['spender_id'] = user.id
- form['amount'] = '66.78'
+ form['amount'] = '74.04'
# Make sure date is today.
today = date.today()
assert form['date'].value == today.strftime('%m/%d/%Y')
form['description'] = 'A test expenditure'
form['shares-0.amount'] = '1'
form['shares-1.amount'] = '2'
- form['shares-2.amount'] = '3'
- form['shares-3.amount'] = '4'
+ form['shares-2.amount'] = '2'
+ form['shares-3.amount'] = '1'
+ for ii in range(4):
+ assert int(form['shares-%d.user_id' % ii].value) == ii + 1
response = form.submit()
response = response.follow()
e = meta.Session.query(model.Expenditure).\
order_by(model.Expenditure.id.desc()).first()
assert e.spender.name == u'Charlie Root'
- assert e.amount == 6678
+ assert e.amount == 7404
assert e.date == today
assert e.description == u'A test expenditure'
# Test the split.
+ shares = dict(((sp.user_id, sp.share)
+ for sp in e.splits))
+ assert shares[1] == Currency('12.34')
+ assert shares[2] == Currency('24.68')
+ assert shares[3] == Currency('24.68')
+ assert shares[4] == Currency('12.34')
def test_edit(self):
order_by(model.Expenditure.id.desc()).first()
assert e.description == u'Updated bundt cake'
+ def test_edit_zero_value(self):
+ user = meta.Session.query(model.User).\
+ filter_by(name=u'Charlie Root').one()
+ e = model.Expenditure(user, 0, u'A zero value expenditure', None)
+ e.even_split()
+ meta.Session.add(e)
+ meta.Session.commit()
+
+ response = self.app.get(url_for(controller='spend',
+ action='edit',
+ id=e.id))
+ response.mustcontain('Edit an Expenditure')
+ form = response.form
+
+ assert int(form['spender_id'].value) == user.id
+ assert form['amount'].value == '0.00'
+ assert form['date'].value == date.today().strftime('%m/%d/%Y')
+ assert form['description'].value == u'A zero value expenditure'
+ for ii in range(4):
+ assert form['shares-%d.amount' % ii].value == '0'
+
+ def test_edit_nonexistent(self):
+ response = self.app.get(url_for(controller='spend',
+ action='edit',
+ id=124234), status=404)
+
+ def test_update_nonexistent(self):
+ response = self.app.post(url_for(controller='spend',
+ action='update',
+ id=14234),
+ params=self.sample_post,
+ status=404)
+
+ def test_all_zero_shares_fails(self):
+ params = self.sample_post.copy()
+ for ii in range(4):
+ params['shares-%d.amount' % ii] = '0'
+ v = ExpenditureSchema()
+ try:
+ v.to_python(params)
+ except Invalid:
+ pass
+
+ def setUp(self):
+ self.sample_post = {
+ 'spender_id': '1',
+ 'amount': '44.12',
+ 'date': '10/5/2008',
+ 'description': 'Example expenditure post data.',
+ 'shares-0.user_id': '1',
+ 'shares-0.amount': '1',
+ 'shares-1.user_id': '2',
+ 'shares-1.amount': '1',
+ 'shares-2.user_id': '3',
+ 'shares-2.amount': '1',
+ 'shares-3.user_id': '4',
+ 'shares-3.amount': '1'}
+
def tearDown(self):
expenditures = meta.Session.query(model.Expenditure).all()
for e in expenditures:
order_by(model.Transfer.id.desc()).first()
assert t.description == u'A new description'
+ def test_edit_nonexistent(self):
+ response = self.app.get(url_for(controller='transfer',
+ action='edit',
+ id=21424), status=404)
+
+ def test_update_nonexistent(self):
+ response = self.app.post(url_for(controller='transfer',
+ action='update',
+ id=21424),
+ params=self.sample_params,
+ status=404)
+
+ def test_update_get_redirects(self):
+ response = self.app.get(url_for(controller='transfer',
+ action='update'),
+ status=302)
+ assert (dict(response.headers)['location'] ==
+ url_for(controller='transfer', action='edit', qualified=True))
+
+ def setUp(self):
+ self.sample_params = {
+ 'debtor_id': '1',
+ 'creditor_id': '2',
+ 'amount': '33.98',
+ 'date': '4/1/2007',
+ 'description': 'Example transfer params.'}
+
def tearDown(self):
transfers = meta.Session.query(model.Transfer).all()
for t in transfers:
--- /dev/null
+from pylons import config
+
+from bluechips.tests import *
+from bluechips import model
+from bluechips.model import meta
+
+class TestUserController(TestController):
+
+ def test_index(self):
+ response = self.app.get(url_for(controller='user'))
+ # Test response...
+ response.mustcontain('Email Notifications', 'User Settings')
+ form = response.form
+ form['new_email'] = 'test@example.com'
+ response = form.submit().follow()
+ response.mustcontain('Updated email address')
+
+ user = meta.Session.query(model.User).\
+ filter_by(username=unicode(config['fake_username'])).one()
+ assert user.email == 'test@example.com'
+
+ def test_clear_email(self):
+ response = self.app.get(url_for(controller='user'))
+ form = response.form
+ form['new_email'] = ''
+ response = form.submit().follow()
+ response.mustcontain('Removed email address')
+
+ user = meta.Session.query(model.User).\
+ filter_by(username=unicode(config['fake_username'])).one()
+ assert user.email == None
+
+
from unittest import TestCase
-from bluechips.tests import *
-from bluechips.model.types import Currency
-from decimal import Decimal
+from bluechips.model import types
class TestCurrency(TestCase):
- def test_initInt(self):
- """
- Make sure the constructor for Currency works
- """
- self.assert_(Currency(1) is Currency(1),
- "Currency objects not interned")
- def test_initString(self):
- """
- Make sure the constructor for Currency works with strings
- """
- self.assertEqual(Currency("0.01"), Currency(1))
- self.assert_(Currency("0.01") is Currency(1),
- "string and int constructors return different values")
-
- def test_string(self):
- """
- Test converting a Currency to a string
- """
- self.assertEqual(str(Currency(1)), "$0.01")
- self.assertEqual(str(Currency(100)), "$1.00")
- self.assertEqual(str(Currency(101)), "$1.01")
- self.assertEqual(str(Currency(-1)), "-$0.01")
- self.assertEqual(str(Currency(-100)), "-$1.00")
- self.assertEqual(str(Currency(-101)), "-$1.01")
-
- def test_stringNoDollar(self):
- """
- Test that Currency values can be retrieved without the dollar sign
- """
- self.assertEqual(Currency(1).__str_no_dollar__(), "0.01")
- self.assertEqual(Currency(100).__str_no_dollar__(), "1.00")
- self.assertEqual(Currency(101).__str_no_dollar__(), "1.01")
- self.assertEqual(Currency(-1).__str_no_dollar__(), "-0.01")
- self.assertEqual(Currency(-100).__str_no_dollar__(), "-1.00")
- self.assertEqual(Currency(-101).__str_no_dollar__(), "-1.01")
-
- def test_additionMath(self):
- """
- Confirm that addition works over currency types and ints
- """
- self.assertEqual(Currency(2) + 2, Currency(4))
- self.assertEqual(2 + Currency(2), Currency(4))
- self.assertEqual(Currency(2) + Currency(2), Currency(4))
-
- def test_additionType(self):
- """
- Adding Currencies or a Currency and an int should yield a
- Currency
- """
- self.assertEqual(type(Currency(2) + 2), Currency)
- self.assertEqual(type(2 + Currency(2)), Currency)
- self.assertEqual(type(Currency(2) + Currency(2)), Currency)
-
- def test_multMath(self):
- """
- This test tests the same 3 things as ``test_addition``, but
- for multiplication
- """
- self.assertEqual(Currency(100) * Decimal("0.25"), Currency(25))
- self.assertEqual(Decimal("0.25") * Currency(100), Currency(25))
- self.assertEqual(Currency(10) * Currency(10), Currency(100))
-
- def test_multType(self):
- """
- The result of multiplying a Currency with something else
- should be a currency
- """
- self.assertEqual(type(Currency(100) * Decimal("0.25")), Currency)
- self.assertEqual(type(Decimal("0.25") * Currency(100)), Currency)
- self.assertEqual(type(Currency(100) * Currency(100)), Currency)
+ def setUp(self):
+ self.c = types.Currency('12.34')
+
+ def test_currency_float(self):
+ assert float(self.c) == 1234.
+
+ def test_currency_int(self):
+ val = int(self.c)
+ assert val == 1234
+ assert type(val) == int
+
+ def test_currency_long(self):
+ val = long(self.c)
+ assert val == 1234
+ assert type(val) == long
--- /dev/null
+from unittest import TestCase
+from formencode import Invalid
+
+from bluechips.model import types
+
+class TestCurrencyValidator(TestCase):
+ def setUp(self):
+ self.v = types.CurrencyValidator()
+
+ def test_currency_validator_good(self):
+ assert (self.v.to_python('12.34') ==
+ types.Currency('12.34'))
+
+ def test_currency_validator_nonzero(self):
+ try:
+ self.v.to_python('0')
+ except Invalid:
+ pass
+
+ def test_currency_validator_precision(self):
+ try:
+ self.v.to_python('12.345')
+ except Invalid:
+ pass
+
+ def test_currency_validator_amount(self):
+ try:
+ self.v.to_python('foo')
+ except Invalid:
+ pass
+from decimal import Decimal
+
from unittest import TestCase
from bluechips import model
+from bluechips.model import meta
from bluechips.model.types import Currency
class TestExpenditure(TestCase):
def setUp(self):
- self.u = model.User('chaz', u'Charles Root', False)
- self.e = model.Expenditure(self.u, Currency('445.27'),
+ self.u = model.User(u'chaz', u'Charles Root', False)
+ self.e = model.Expenditure(self.u, Currency('444.88'),
u'chaz buys lunch')
+ meta.Session.add(self.u)
+ meta.Session.add(self.e)
+ meta.Session.commit()
def test_constructor(self):
assert self.e.spender == self.u
- assert self.e.amount == Currency('445.27')
+ assert self.e.amount == Currency('444.88')
assert self.e.description == u'chaz buys lunch'
def test_repr(self):
assert (repr(self.e) ==
- '<Expenditure: spender: Charles Root spent: $445.27>')
+ '<Expenditure: spender: Charles Root spent: $444.88>')
+
+ def test_even_split(self):
+ self.e.even_split()
+ meta.Session.commit()
+ for sp in self.e.splits:
+ assert sp.share == Currency('111.22')
+
+ def test_split_change_to_zero(self):
+ self.e.even_split()
+ meta.Session.commit()
+ users = meta.Session.query(model.User).all()
+ split_dict = dict((user, Decimal('0')) for user in users)
+ split_dict[self.u] = Decimal(1)
+ self.e.split(split_dict)
+
+ def _two_way_split_test(self, amount, min, max):
+ e2 = model.Expenditure(self.u, amount,
+ u'testing splits')
+ u2 = model.User(u'bo', u'Bo Jangles', False)
+ meta.Session.add(u2)
+ meta.Session.add(e2)
+ meta.Session.commit()
+ split_dict = {}
+ split_dict[self.u] = Decimal(1)
+ split_dict[u2] = Decimal(1)
+ e2.split(split_dict)
+ assert min <= e2.share(u2) <= max
+ meta.Session.delete(e2)
+ meta.Session.delete(u2)
+ meta.Session.commit()
+
+ def test_split_rounds_down(self):
+ self._two_way_split_test(Currency('40.01'),
+ Currency('20.00'),
+ Currency('20.01'))
+
+ def test_split_rounds_up(self):
+ self._two_way_split_test(Currency('39.99'),
+ Currency('19.99'),
+ Currency('20.00'))
+
+ def test_split_small(self):
+ self._two_way_split_test(Currency('0.01'),
+ Currency('0.00'),
+ Currency('0.01'))
+
+ def test_split_small_negative(self):
+ self._two_way_split_test(Currency('-0.01'),
+ Currency('-0.01'),
+ Currency('-0.00'))
+
+ def tearDown(self):
+ meta.Session.delete(self.e)
+ meta.Session.delete(self.u)
+ meta.Session.commit()
--- /dev/null
+from unittest import TestCase
+from bluechips import model
+from bluechips.model.types import Currency
+
+class TestSplit(TestCase):
+ def setUp(self):
+ self.u = model.User('chaz', u'Charles Root', False)
+ self.e = model.Expenditure(self.u, Currency('12.34'),
+ u'A test expenditure')
+ self.sp = model.Split(self.e, self.u, Currency('5.55'))
+
+ def test_constructor(self):
+ assert self.sp.expenditure == self.e
+ assert self.sp.user == self.u
+ assert self.sp.share == Currency('5.55')
+
+ def test_repr(self):
+ assert (repr(self.sp) == '<Split: expense: %s user: %s share: %s>' %
+ (self.sp.expenditure, self.sp.user, self.sp.share))
beaker.session.secret = somesecret
fake_username = root
+testing = true
sqlalchemy.url = sqlite://