Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 108 additions & 27 deletions abcbank/account.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,124 @@
from abcbank.transaction import Transaction
from transaction import Transaction
from datetime import datetime

CHECKING = 0
SAVINGS = 1
MAXI_SAVINGS = 2


class Account:
def __init__(self, accountType):
self.accountType = accountType
class Account(object):
def __init__(self):
self.transactions = []

def deposit(self, amount):
#Added account argument to allow deposit from another account
# but no withdraw from another account;
def deposit(self, amount, txnDate=None, account=None):
if (amount <= 0):
raise ValueError("amount must be greater than zero")
else:
self.transactions.append(Transaction(amount))
if account:
account.transactions.append(Transaction(amount))
elif account and txnDate:
account.transactions.append(Transaction(amount, txnDate))
elif txnDate:
self.transactions.append(Transaction(amount, txnDate))
else:
self.transactions.append(Transaction(amount))

def withdraw(self, amount):
def withdraw(self, amount, txnDate=None):
if (amount <= 0):
raise ValueError("amount must be greater than zero")
elif self.sumTransactions() - amount < 0:
raise ValueError("Insufficient Funds")
elif txnDate:
self.transactions.append(Transaction(-amount, txnDate))
else:
self.transactions.append(Transaction(-amount))

#To allow a customer to transfer from one of his account to another
def transfer(self, amount, toAccount, txnDate=None):
if (amount <= 0):
raise ValueError("amount must be greater than zero")
else:
self.withdraw(amount, txnDate)
self.deposit(amount, txnDate, toAccount)

def interestEarned(self):
amount = self.sumTransactions()
if self.accountType == SAVINGS:
if (amount <= 1000):
return amount * 0.001
else:
return 1 + (amount - 1000) * 0.002
if self.accountType == MAXI_SAVINGS:
if (amount <= 1000):
return amount * 0.02
elif (amount <= 2000):
return 20 + (amount - 1000) * 0.05
pass

#This was done in a hurry and needs to be cleaned up better
def dailyInterest(self, yrlyInterest=None, baseAmt=None, yrlyInterest2=None):
# amount = self.sumTransactions()
# return amount * 0.001
totalInterest = 0.0

for txnNum in range(len(self.transactions)):
if txnNum: #No interest with first transaction
continue
accruedAmt = self.sumTransactions(self.transactions[txnNum].transactionDate)
# Add interest so far also to the current principal
accruedAmt += totalInterest
daysBetween = (self.transactions[txnNum].transactionDate - self.transactions[txnNum-1].transactionDate).days
# Need to account for leap year later,
# as we need to include interests accrued in days
# spread between say a leap and non leap year
if not baseAmt:
totalInterest = accruedAmt * daysBetween * yrlyInterest / 365
elif accruedAmt < baseAmt:
totalInterest = accruedAmt * daysBetween * yrlyInterest / 365
else:
return 70 + (amount - 2000) * 0.1
totalInterest = accruedAmt * daysBetween * yrlyInterest2 / 365


#Calculate the interest from the last txn till today.
daysLastTxn = (datetime.now() - self.transactions[len(self.transactions) -1].transactionDate).days
totalInterest += self.sumTransactions() * daysLastTxn * yrlyInterest / 365

return totalInterest

def sumTransactions(self, tillDate=None):
if not tillDate:
return sum([t.amount for t in self.transactions])
txnSum = 0.0
for txn in self.transactions:
if txn.transactionDate < tillDate:
txnSum += txn.amount;
return txnSum

def recentWithdrawal(self):
lastWithdrawal = None
for txn in self.transactions:
if txn.amount < 0:
if not lastWithdrawal:
lastWithdrawal = txn.transactionDate
elif lastWithdrawal < txn.transactionDate:
lastWithdrawal = txn.transactionDate
if lastWithdrawal:
last = datetime.now() - lastWithdrawal
return ((last.days - 10) < 10)
else:
return amount * 0.001
return False

class SavingsAc(Account):
def interestEarned(self):
amount = self.sumTransactions()
# if (amount <= 1000):
# return amount * 0.001
# else:
# return 1 + (amount - 1000) * 0.002
return self.dailyInterest(0.001, 1000, 0.002)

class CheckingAc(Account):
def interestEarned(self):
return self.dailyInterest(0.001)

def sumTransactions(self, checkAllTransactions=True):
return sum([t.amount for t in self.transactions])
class MaxiSavingsAc(Account):
def interestEarned(self):
amount = self.sumTransactions()
# Changing logic to have different interest rates
# based on recent activity
if self.recentWithdrawal():
return self.dailyInterest(0.001)
else:
return self.dailyInterest(0.05)
# if (amount <= 1000):
# return amount * 0.02
# elif (amount <= 2000):
# return 20 + (amount - 1000) * 0.05
# else:
# return 70 + (amount - 2000) * 0.1
9 changes: 4 additions & 5 deletions abcbank/customer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from account import CHECKING, SAVINGS, MAXI_SAVINGS

from abcbank.account import SavingsAc, CheckingAc, MaxiSavingsAc

class Customer:
def __init__(self, name):
Expand Down Expand Up @@ -30,11 +29,11 @@ def getStatement(self):

def statementForAccount(self, account):
accountType = "\n\n\n"
if account.accountType == CHECKING:
if type(account).__name__ == "CheckingAc":
accountType = "\n\nChecking Account\n"
if account.accountType == SAVINGS:
if type(account).__name__ == "SavingsAc":
accountType = "\n\nSavings Account\n"
if account.accountType == MAXI_SAVINGS:
if type(account).__name__ == "MaxiSavingsAc":
accountType = "\n\nMaxi Savings Account\n"
transactionSummary = [self.withdrawalOrDepositText(t) + " " + _toDollars(abs(t.amount))
for t in account.transactions]
Expand Down
9 changes: 6 additions & 3 deletions abcbank/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@


class Transaction:
def __init__(self, amount):
self.amount = amount
self.transactionDate = datetime.now()
def __init__(self, amount, txnDate=None):
self.amount = amount
if txnDate:
self.transactionDate = txnDate
else:
self.transactionDate = datetime.now()
4 changes: 4 additions & 0 deletions runTests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
find . -iname \*.pyc|xargs rm -f
nosetests

122 changes: 86 additions & 36 deletions tests/bank_tests.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,88 @@
from nose.tools import assert_equals

from account import Account, CHECKING, MAXI_SAVINGS, SAVINGS
from bank import Bank
from customer import Customer


def test_customer_summary():
bank = Bank()
john = Customer("John").openAccount(Account(CHECKING))
bank.addCustomer(john)
assert_equals(bank.customerSummary(),
"Customer Summary\n - John (1 account)")


def test_checking_account():
bank = Bank()
checkingAccount = Account(CHECKING)
bill = Customer("Bill").openAccount(checkingAccount)
bank.addCustomer(bill)
checkingAccount.deposit(100.0)
assert_equals(bank.totalInterestPaid(), 0.1)


def test_savings_account():
bank = Bank()
checkingAccount = Account(SAVINGS)
bank.addCustomer(Customer("Bill").openAccount(checkingAccount))
checkingAccount.deposit(1500.0)
assert_equals(bank.totalInterestPaid(), 2.0)


def test_maxi_savings_account():
bank = Bank()
checkingAccount = Account(MAXI_SAVINGS)
bank.addCustomer(Customer("Bill").openAccount(checkingAccount))
checkingAccount.deposit(3000.0)
assert_equals(bank.totalInterestPaid(), 170.0)
from abcbank.account import SavingsAc, CheckingAc, MaxiSavingsAc
from abcbank.bank import Bank
from abcbank.customer import Customer

from datetime import datetime

# The following module is only available from python 3.3
# but can be used to mock today's date so that all tests
# complete successfully
#
#
# import mock

# def mocked_get_now(timezone):
# dt = datetime.strptime('May 10 2016 11:00PM', '%b %d %Y %I:%M%p')
# return timezone.localize(dt)

# @mock.patch('path.to.your.models.MyClass.get_now', side_effect=mocked_get_now)

class TestBank:

def setUp(self):
self.bank = Bank()

def tearDown(self):
pass

def test_customer_summary(self):
john = Customer("John").openAccount(SavingsAc())
self.bank.addCustomer(john)
assert_equals(self.bank.customerSummary(),
"Customer Summary\n - John (1 account)")

def test_checking_account(self):
checkingAccount = CheckingAc()
bill = Customer("Bill").openAccount(checkingAccount)
self.bank.addCustomer(bill)
txnDate = datetime.strptime('May 1 2016 10:14AM', '%b %d %Y %I:%M%p')
checkingAccount.deposit(100.0, txnDate)
txnDate = datetime.strptime('May 5 2016 3:21PM', '%b %d %Y %I:%M%p')
checkingAccount.deposit(200.0, txnDate)
#since we moved over to daily interest and the total interest
# is calculated on a daily basis, this result will change
# assert_equals(self.bank.totalInterestPaid(), 0.1)
assert_equals(self.bank.totalInterestPaid(), 0.00410958904109589)


def test_savings_account(self):
savingsAccount = SavingsAc()
self.bank.addCustomer(Customer("Bill").openAccount(savingsAccount))
txnDate = datetime.strptime('May 1 2012 10:14AM', '%b %d %Y %I:%M%p')
savingsAccount.deposit(100.0, txnDate)
assert_equals(self.bank.totalInterestPaid(), 0.40273972602739727)

def test_maxi_savings_account(self):
maxiSavingsAccount = MaxiSavingsAc()
self.bank.addCustomer(Customer("Bill").openAccount(maxiSavingsAccount))
txnDate = datetime.strptime('Feb 5 2012 4:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.deposit(3000.0,txnDate)
# Different interest after maxi interst calculation logic is changed
# assert_equals(self.bank.totalInterestPaid(), 170.0)
assert_equals(self.bank.totalInterestPaid(), 639.4520547945206)

def test_maxi_savings_account_recent_withdrawal(self):
maxiSavingsAccount = MaxiSavingsAc()
self.bank.addCustomer(Customer("Bill").openAccount(maxiSavingsAccount))
txnDate = datetime.strptime('Feb 5 2012 3:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.deposit(200.0, txnDate)
txnDate = datetime.strptime('Feb 5 2012 4:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.deposit(3000.0, txnDate)
# Putting a date in the future, which should fail elsewhere in the ideal
# scenario, but this is to ensure that this test case passes for a longtime
txnDate = datetime.strptime('May 9 2016 3:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.withdraw(100.0, txnDate)
assert_equals(self.bank.totalInterestPaid(), 0.008493150684931507)

def test_maxi_savings_account_nonrecent_withdrawal(self):
maxiSavingsAccount = MaxiSavingsAc()
self.bank.addCustomer(Customer("Bill").openAccount(maxiSavingsAccount))
txnDate = datetime.strptime('Feb 5 2012 3:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.deposit(200.0, txnDate)
txnDate = datetime.strptime('Feb 5 2012 4:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.deposit(3000.0, txnDate)
txnDate = datetime.strptime('Mar 19 2012 3:21PM', '%b %d %Y %I:%M%p')
maxiSavingsAccount.withdraw(100.0, txnDate)
assert_equals(self.bank.totalInterestPaid(), 642.5068493150685)
Loading