diff --git a/12-Final Capstone Python Project/.ipynb_checkpoints/03-Final Capstone Suggested Walkthrough-checkpoint.ipynb b/12-Final Capstone Python Project/.ipynb_checkpoints/03-Final Capstone Suggested Walkthrough-checkpoint.ipynb
new file mode 100644
index 000000000..1e3674c27
--- /dev/null
+++ b/12-Final Capstone Python Project/.ipynb_checkpoints/03-Final Capstone Suggested Walkthrough-checkpoint.ipynb
@@ -0,0 +1,742 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Final Capstone Project - Suggested Walkthrough:\n",
+ "\n",
+ "This is a suggested method for handling one of the Final Capstone Projects. We start by coding out the strictest requirements, and then build out from a working baseline model. Feel free to adapt this solution, and add features you think could help. Good luck!\n",
+ "\n",
+ "\n",
+ "## Bank Account Manager\n",
+ "\n",
+ "Under the Classes section in the list of suggested final capstone projects is a Bank Account Manager program. The goal is to create a class called Account which will be an abstract class for three other classes called CheckingAccount, SavingsAccount and BusinessAccount. Then you should manage credits and debits from these accounts through an ATM style program."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Project Scope\n",
+ "To tackle this project, first consider what has to happen.\n",
+ "1. There will be three different types of bank account (Checking, Savings, Business)\n",
+ "2. Each account will accept deposits and withdrawals, and will need to report balances"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Project Wishlist\n",
+ "We might consider additional features, like:\n",
+ "* impose a monthly maintenance fee\n",
+ "* waive fees for minimum combined deposit balances\n",
+ "* each account may have additional properties unique to that account:\n",
+ " * Checking allows unlimited transactions, and may keep track of printed checks\n",
+ " * Savings limits the number of withdrawals per period, and may earn interest\n",
+ " * Business may impose transaction fees\n",
+ "* automatically transfer the \"change\" for debit card purchases from Checking to Savings,
where \"change\" is the amount needed to raise a debit to the nearest whole dollar\n",
+ "* permit savings autodraft overdraft protection\n",
+ "\n",
+ "### Let's get started!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 1: Establish an abstract Account class with features shared by all accounts.\n",
+ "Note that abstract classes are never instantiated, they simply provide a base class with attributes and methods to be inherited by any derived class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Account:\n",
+ " # Define an __init__ constructor method with attributes shared by all accounts:\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " self.acct_nbr = acct_nbr\n",
+ " self.balance = opening_deposit\n",
+ " \n",
+ " # Define a __str__ mehthod to return a recognizable string to any print() command\n",
+ " def __str__(self):\n",
+ " return f'${self.balance:.2f}'\n",
+ " \n",
+ " # Define a universal method to accept deposits\n",
+ " def deposit(self,dep_amt):\n",
+ " self.balance += dep_amt\n",
+ "\n",
+ " # Define a universal method to handle withdrawals\n",
+ " def withdraw(self,wd_amt):\n",
+ " if self.balance >= wd_amt:\n",
+ " self.balance -= wd_amt\n",
+ " else:\n",
+ " return 'Funds Unavailable'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 2: Establish a Checking Account class that inherits from Account, and adds Checking-specific traits."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Checking(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Checking accounts\n",
+ " def __str__(self):\n",
+ " return f'Checking Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 3: TEST setting up a Checking Account object"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = Checking(54321,654.33)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #54321\n",
+ " Balance: $654.33\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(x)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Funds Unavailable'"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x.withdraw(1000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x.withdraw(30)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "624.33"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x.balance"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 4: Set up similar Savings and Business account classes"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Savings(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Savings accounts\n",
+ " def __str__(self):\n",
+ " return f'Savings Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ "\n",
+ "class Business(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Business accounts\n",
+ " def __str__(self):\n",
+ " return f'Business Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**At this point** we've met the minimum requirement for the assignment. We have three different bank account classes. Each one can accept deposits, make withdrawals and report a balance, as they each inherit from an abstract Account base class.\n",
+ "\n",
+ "So now the fun part - let's add some features!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 5: Create a Customer class\n",
+ "\n",
+ "For this next phase, let's set up a Customer class that has a customer ID and can contain any number and/or combination of Account objects."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Customer:\n",
+ " def __init__(self, name, cust_id):\n",
+ " self.name = name\n",
+ " self.cust_id = cust_id\n",
+ " \n",
+ " # Create a dictionary of accounts, with lists to hold multiple accounts\n",
+ " self.accts = {'C':[],'S':[],'B':[]}\n",
+ " \n",
+ " def open_checking(self,acct_nbr,opening_deposit):\n",
+ " self.accts['C'].append(Checking(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_savings(self,acct_nbr,opening_deposit):\n",
+ " self.accts['S'].append(Savings(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_business(self,acct_nbr,opening_deposit):\n",
+ " self.accts['B'].append(Business(acct_nbr,opening_deposit))\n",
+ " \n",
+ " # rather than maintain a running total of deposit balances,\n",
+ " # write a method that computes a total as needed\n",
+ " def get_total_deposits(self):\n",
+ " total = 0\n",
+ " for acct in self.accts['C']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['S']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['B']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " print(f'Combined Deposits: ${total}')\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 6: TEST setting up a Customer, adding accounts, and checking balances"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob = Customer('Bob',1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob.open_checking(321,555.55)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #321\n",
+ " Balance: $555.55\n",
+ "Combined Deposits: $555.55\n"
+ ]
+ }
+ ],
+ "source": [
+ "bob.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob.open_savings(564,444.66)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #321\n",
+ " Balance: $555.55\n",
+ "Savings Account #564\n",
+ " Balance: $444.66\n",
+ "Combined Deposits: $1000.21\n"
+ ]
+ }
+ ],
+ "source": [
+ "bob.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nancy = Customer('Nancy',2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nancy.open_business(2018,8900)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Wait!** Why don't Nancy's combined deposits show a decimal?
This is easily fixed in the class definition (mostly copied from above, with a change made to the last line of code):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Customer:\n",
+ " def __init__(self, name, cust_id):\n",
+ " self.name = name\n",
+ " self.cust_id = cust_id\n",
+ " self.accts = {'C':[],'S':[],'B':[]}\n",
+ " \n",
+ " def open_checking(self,acct_nbr,opening_deposit):\n",
+ " self.accts['C'].append(Checking(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_savings(self,acct_nbr,opening_deposit):\n",
+ " self.accts['S'].append(Savings(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_business(self,acct_nbr,opening_deposit):\n",
+ " self.accts['B'].append(Business(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def get_total_deposits(self):\n",
+ " total = 0\n",
+ " for acct in self.accts['C']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['S']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['B']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " print(f'Combined Deposits: ${total:.2f}') # added precision formatting here"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**So it's fixed, right?**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Nope!** Changes made to the class definition do *not* affect objects created under different sets of instructions.
To fix Nancy's account, we have to build her record from scratch."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy = Customer('Nancy',2)\n",
+ "nancy.open_business(2018,8900)\n",
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### This is why testing is so important!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 7: Let's write some functions for making deposits and withdrawals.\n",
+ "\n",
+ "Be sure to include a docstring that explains what's expected by the function!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_dep(cust,acct_type,acct_num,dep_amt):\n",
+ " \"\"\"\n",
+ " make_dep(cust, acct_type, acct_num, dep_amt)\n",
+ " cust = variable name (Customer record/ID)\n",
+ " acct_type = string 'C' 'S' or 'B'\n",
+ " acct_num = integer\n",
+ " dep_amt = integer\n",
+ " \"\"\"\n",
+ " for acct in cust.accts[acct_type]:\n",
+ " if acct.acct_nbr == acct_num:\n",
+ " acct.deposit(dep_amt)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "make_dep(nancy,'B',2018,67.45)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8967.45\n",
+ "Combined Deposits: $8967.45\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_wd(cust,acct_type,acct_num,wd_amt):\n",
+ " \"\"\"\n",
+ " make_dep(cust, acct_type, acct_num, wd_amt)\n",
+ " cust = variable name (Customer record/ID)\n",
+ " acct_type = string 'C' 'S' or 'B'\n",
+ " acct_num = integer\n",
+ " wd_amt = integer\n",
+ " \"\"\"\n",
+ " for acct in cust.accts[acct_type]:\n",
+ " if acct.acct_nbr == acct_num:\n",
+ " acct.withdraw(wd_amt)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "make_wd(nancy,'B',2018,1000000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8967.45\n",
+ "Combined Deposits: $8967.45\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**What happened??** We seemed to successfully make a withdrawal, but nothing changed!
This is because, at the very beginning, we had our Account class *return* the string 'Funds Unavailable' instead of print it. If we change that here, we'll have to also run the derived class definitions, and Nancy's creation, but *not* the Customer class definition. Watch:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Account:\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " self.acct_nbr = acct_nbr\n",
+ " self.balance = opening_deposit\n",
+ " \n",
+ " def __str__(self):\n",
+ " return f'${self.balance:.2f}'\n",
+ " \n",
+ " def deposit(self,dep_amt):\n",
+ " self.balance += dep_amt\n",
+ " \n",
+ " def withdraw(self,wd_amt):\n",
+ " if self.balance >= wd_amt:\n",
+ " self.balance -= wd_amt\n",
+ " else:\n",
+ " print('Funds Unavailable') # changed \"return\" to \"print\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Checking(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ " \n",
+ " def __str__(self):\n",
+ " return f'Checking Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ " \n",
+ "class Savings(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " def __str__(self):\n",
+ " return f'Savings Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ "\n",
+ "class Business(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " def __str__(self):\n",
+ " return f'Business Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy = Customer('Nancy',2)\n",
+ "nancy.open_business(2018,8900)\n",
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Funds Unavailable\n"
+ ]
+ }
+ ],
+ "source": [
+ "make_wd(nancy,'B',2018,1000000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Good job!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/12-Final Capstone Python Project/03-Final Capstone Suggested Walkthrough.ipynb b/12-Final Capstone Python Project/03-Final Capstone Suggested Walkthrough.ipynb
new file mode 100644
index 000000000..1e3674c27
--- /dev/null
+++ b/12-Final Capstone Python Project/03-Final Capstone Suggested Walkthrough.ipynb
@@ -0,0 +1,742 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Final Capstone Project - Suggested Walkthrough:\n",
+ "\n",
+ "This is a suggested method for handling one of the Final Capstone Projects. We start by coding out the strictest requirements, and then build out from a working baseline model. Feel free to adapt this solution, and add features you think could help. Good luck!\n",
+ "\n",
+ "\n",
+ "## Bank Account Manager\n",
+ "\n",
+ "Under the Classes section in the list of suggested final capstone projects is a Bank Account Manager program. The goal is to create a class called Account which will be an abstract class for three other classes called CheckingAccount, SavingsAccount and BusinessAccount. Then you should manage credits and debits from these accounts through an ATM style program."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Project Scope\n",
+ "To tackle this project, first consider what has to happen.\n",
+ "1. There will be three different types of bank account (Checking, Savings, Business)\n",
+ "2. Each account will accept deposits and withdrawals, and will need to report balances"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Project Wishlist\n",
+ "We might consider additional features, like:\n",
+ "* impose a monthly maintenance fee\n",
+ "* waive fees for minimum combined deposit balances\n",
+ "* each account may have additional properties unique to that account:\n",
+ " * Checking allows unlimited transactions, and may keep track of printed checks\n",
+ " * Savings limits the number of withdrawals per period, and may earn interest\n",
+ " * Business may impose transaction fees\n",
+ "* automatically transfer the \"change\" for debit card purchases from Checking to Savings,
where \"change\" is the amount needed to raise a debit to the nearest whole dollar\n",
+ "* permit savings autodraft overdraft protection\n",
+ "\n",
+ "### Let's get started!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 1: Establish an abstract Account class with features shared by all accounts.\n",
+ "Note that abstract classes are never instantiated, they simply provide a base class with attributes and methods to be inherited by any derived class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Account:\n",
+ " # Define an __init__ constructor method with attributes shared by all accounts:\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " self.acct_nbr = acct_nbr\n",
+ " self.balance = opening_deposit\n",
+ " \n",
+ " # Define a __str__ mehthod to return a recognizable string to any print() command\n",
+ " def __str__(self):\n",
+ " return f'${self.balance:.2f}'\n",
+ " \n",
+ " # Define a universal method to accept deposits\n",
+ " def deposit(self,dep_amt):\n",
+ " self.balance += dep_amt\n",
+ "\n",
+ " # Define a universal method to handle withdrawals\n",
+ " def withdraw(self,wd_amt):\n",
+ " if self.balance >= wd_amt:\n",
+ " self.balance -= wd_amt\n",
+ " else:\n",
+ " return 'Funds Unavailable'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 2: Establish a Checking Account class that inherits from Account, and adds Checking-specific traits."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Checking(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Checking accounts\n",
+ " def __str__(self):\n",
+ " return f'Checking Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 3: TEST setting up a Checking Account object"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = Checking(54321,654.33)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #54321\n",
+ " Balance: $654.33\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(x)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Funds Unavailable'"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x.withdraw(1000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x.withdraw(30)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "624.33"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x.balance"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 4: Set up similar Savings and Business account classes"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Savings(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Savings accounts\n",
+ " def __str__(self):\n",
+ " return f'Savings Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ "\n",
+ "class Business(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " # Run the base class __init__\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " # Define a __str__ method that returns a string specific to Business accounts\n",
+ " def __str__(self):\n",
+ " return f'Business Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**At this point** we've met the minimum requirement for the assignment. We have three different bank account classes. Each one can accept deposits, make withdrawals and report a balance, as they each inherit from an abstract Account base class.\n",
+ "\n",
+ "So now the fun part - let's add some features!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 5: Create a Customer class\n",
+ "\n",
+ "For this next phase, let's set up a Customer class that has a customer ID and can contain any number and/or combination of Account objects."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Customer:\n",
+ " def __init__(self, name, cust_id):\n",
+ " self.name = name\n",
+ " self.cust_id = cust_id\n",
+ " \n",
+ " # Create a dictionary of accounts, with lists to hold multiple accounts\n",
+ " self.accts = {'C':[],'S':[],'B':[]}\n",
+ " \n",
+ " def open_checking(self,acct_nbr,opening_deposit):\n",
+ " self.accts['C'].append(Checking(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_savings(self,acct_nbr,opening_deposit):\n",
+ " self.accts['S'].append(Savings(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_business(self,acct_nbr,opening_deposit):\n",
+ " self.accts['B'].append(Business(acct_nbr,opening_deposit))\n",
+ " \n",
+ " # rather than maintain a running total of deposit balances,\n",
+ " # write a method that computes a total as needed\n",
+ " def get_total_deposits(self):\n",
+ " total = 0\n",
+ " for acct in self.accts['C']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['S']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['B']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " print(f'Combined Deposits: ${total}')\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 6: TEST setting up a Customer, adding accounts, and checking balances"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob = Customer('Bob',1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob.open_checking(321,555.55)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #321\n",
+ " Balance: $555.55\n",
+ "Combined Deposits: $555.55\n"
+ ]
+ }
+ ],
+ "source": [
+ "bob.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bob.open_savings(564,444.66)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Checking Account #321\n",
+ " Balance: $555.55\n",
+ "Savings Account #564\n",
+ " Balance: $444.66\n",
+ "Combined Deposits: $1000.21\n"
+ ]
+ }
+ ],
+ "source": [
+ "bob.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nancy = Customer('Nancy',2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nancy.open_business(2018,8900)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Wait!** Why don't Nancy's combined deposits show a decimal?
This is easily fixed in the class definition (mostly copied from above, with a change made to the last line of code):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Customer:\n",
+ " def __init__(self, name, cust_id):\n",
+ " self.name = name\n",
+ " self.cust_id = cust_id\n",
+ " self.accts = {'C':[],'S':[],'B':[]}\n",
+ " \n",
+ " def open_checking(self,acct_nbr,opening_deposit):\n",
+ " self.accts['C'].append(Checking(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_savings(self,acct_nbr,opening_deposit):\n",
+ " self.accts['S'].append(Savings(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def open_business(self,acct_nbr,opening_deposit):\n",
+ " self.accts['B'].append(Business(acct_nbr,opening_deposit))\n",
+ " \n",
+ " def get_total_deposits(self):\n",
+ " total = 0\n",
+ " for acct in self.accts['C']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['S']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " for acct in self.accts['B']:\n",
+ " print(acct)\n",
+ " total += acct.balance\n",
+ " print(f'Combined Deposits: ${total:.2f}') # added precision formatting here"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**So it's fixed, right?**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Nope!** Changes made to the class definition do *not* affect objects created under different sets of instructions.
To fix Nancy's account, we have to build her record from scratch."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy = Customer('Nancy',2)\n",
+ "nancy.open_business(2018,8900)\n",
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### This is why testing is so important!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Step 7: Let's write some functions for making deposits and withdrawals.\n",
+ "\n",
+ "Be sure to include a docstring that explains what's expected by the function!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_dep(cust,acct_type,acct_num,dep_amt):\n",
+ " \"\"\"\n",
+ " make_dep(cust, acct_type, acct_num, dep_amt)\n",
+ " cust = variable name (Customer record/ID)\n",
+ " acct_type = string 'C' 'S' or 'B'\n",
+ " acct_num = integer\n",
+ " dep_amt = integer\n",
+ " \"\"\"\n",
+ " for acct in cust.accts[acct_type]:\n",
+ " if acct.acct_nbr == acct_num:\n",
+ " acct.deposit(dep_amt)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "make_dep(nancy,'B',2018,67.45)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8967.45\n",
+ "Combined Deposits: $8967.45\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_wd(cust,acct_type,acct_num,wd_amt):\n",
+ " \"\"\"\n",
+ " make_dep(cust, acct_type, acct_num, wd_amt)\n",
+ " cust = variable name (Customer record/ID)\n",
+ " acct_type = string 'C' 'S' or 'B'\n",
+ " acct_num = integer\n",
+ " wd_amt = integer\n",
+ " \"\"\"\n",
+ " for acct in cust.accts[acct_type]:\n",
+ " if acct.acct_nbr == acct_num:\n",
+ " acct.withdraw(wd_amt)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "make_wd(nancy,'B',2018,1000000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8967.45\n",
+ "Combined Deposits: $8967.45\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**What happened??** We seemed to successfully make a withdrawal, but nothing changed!
This is because, at the very beginning, we had our Account class *return* the string 'Funds Unavailable' instead of print it. If we change that here, we'll have to also run the derived class definitions, and Nancy's creation, but *not* the Customer class definition. Watch:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Account:\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " self.acct_nbr = acct_nbr\n",
+ " self.balance = opening_deposit\n",
+ " \n",
+ " def __str__(self):\n",
+ " return f'${self.balance:.2f}'\n",
+ " \n",
+ " def deposit(self,dep_amt):\n",
+ " self.balance += dep_amt\n",
+ " \n",
+ " def withdraw(self,wd_amt):\n",
+ " if self.balance >= wd_amt:\n",
+ " self.balance -= wd_amt\n",
+ " else:\n",
+ " print('Funds Unavailable') # changed \"return\" to \"print\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Checking(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ " \n",
+ " def __str__(self):\n",
+ " return f'Checking Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ " \n",
+ "class Savings(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " def __str__(self):\n",
+ " return f'Savings Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'\n",
+ "\n",
+ "\n",
+ "class Business(Account):\n",
+ " def __init__(self,acct_nbr,opening_deposit):\n",
+ " super().__init__(acct_nbr,opening_deposit)\n",
+ "\n",
+ " def __str__(self):\n",
+ " return f'Business Account #{self.acct_nbr}\\n Balance: {Account.__str__(self)}'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy = Customer('Nancy',2)\n",
+ "nancy.open_business(2018,8900)\n",
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Funds Unavailable\n"
+ ]
+ }
+ ],
+ "source": [
+ "make_wd(nancy,'B',2018,1000000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Business Account #2018\n",
+ " Balance: $8900.00\n",
+ "Combined Deposits: $8900.00\n"
+ ]
+ }
+ ],
+ "source": [
+ "nancy.get_total_deposits()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Good job!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}