Python beginner here. Building an OOP bank account program with SQLite and starting to struggle a bit with its design. From a design standpoint I have a file (bank) which calls ATM. ATM simulates an ATM, and is the file which then calls bank_account or credit_card depending on the account data passed in from bank.
To initially setup an account I decided to put this into a different file, for example bank_account_setup or for credit_card, credit_card_setup. These would create_account, help setup pin etc so that the account is created and ready to use. Then the actual bank_account or credit_card contains other functions, like, deposit, withdraw, get_balance etc. Also, send_email is in another file
My question is basically around my design. Is there a way to structure this better? How about my setup files to create bank or credit card accounts? Is that a good or bad idea? Also, another issue I am having is that when I run bank I pass in account type to ATM. In ATM I then had to know what class I am using in advance and instantiate that inside ATM. Could I handle that dynamically? (Also, the code does work - just concerned with bad design).
Here's my calling file bank:
import atm
class BankAccount:
def __init__(self, name, social, account_number, balance, acctype):
self.name = name
self.social = social
self.account_number = account_number
self.balance = balance
self.acctype = acctype
if __name__ == '__main__':
obj1 = atm.ATM.main_menu( "Frank Smith", 135063522, 5544, 850, 'credit_card', 4400110022004400)
Here's ATM, which calls the other files:
import sqlite3, smtplib
import bank_account
import secrets
import send_email
import credit_card
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
conn = sqlite3.connect('bank_account.db')
c = conn.cursor()
class ATM:
def get_pin(account_number, acctype, user_pin):
with conn:
db_pin = c.execute("SELECT pin from {} WHERE account_number=:account_number".format(acctype),
{'account_number':account_number})
db_pin = c.fetchone()
if db_pin is not None:
return(db_pin[0])
else:
print("db pin is None")
pass
def set_pin(account_number, acctype, input_code):
with conn:
get_code = ATM.get_user_code(account_number, acctype)
if get_code is None:
pass
print("You need to request an authorization code first before you set your pin")
else:
if get_code !=input_code:
print("Authorization code not valid")
else:
pin = input("Please set your 4 digit pin: ")
if len(pin) < 4 or len(pin) >4 or len(pin) == 4 and pin.isdigit()==False:
print("This is not a 4 digit pin")
else:
print("pin accepted")
c.execute("""UPDATE {} SET pin=:pin
WHERE account_number =:account_number""".format(acctype),
{'account_number':account_number, 'pin':pin})
print("Pin for account has been updated")
def main_menu(name, social, account_number, balance, acctype, card_no=None):
# obj1 = bank_account.BankAccount(name, social, account_number, balance, acctype)
# obj1 = credit_card.CreditCard(name, social, account_number, balance, acctype, card_no)
user_pin = int(input("\nATM Home Screen. Please enter your pin code: "))
db_pin = ATM.get_pin(account_number, acctype, user_pin)
if user_pin != db_pin and db_pin != '':
print("No pin match")
elif db_pin is '':
print("Pin has not been set")
print("First request an authorization code and use that to set the pin")
else:
user_pin == db_pin
print("\nPin accepted continue \n ")
print("""""""ATM Menu, choose an option""""""")
print("\n1 - Deposit funds")
print("2 - Withdraw funds")
print("3 - Check balance")
print("4 - Reset Pin")
print("5 - Exit")
while True:
try:
choice = int(input("Please enter a number: "))
except ValueError:
print("This is not a number")
if choice >= 1 and choice <=5:
if choice == 1:
amount = input("\nPlease enter the deposit amount: ")
if amount != '' and amount.isdigit():
int(amount)
obj1.deposit( account_number, acctype, amount)
else:
print("Please enter a valid number")
elif choice == 2:
amount = input("Please enter the withdrawl amount: ")
if amount != '' and amount.isdigit():
int(amount)
obj1.withdraw(account_number, acctype, amount)
else:
print("Please enter a valid number")
elif choice ==3:
obj1.get_balance(account_number, acctype)
elif choice ==4:
new_pin = input("Please enter a new 4 digit pin: ")
if new_pin != '' and new_pin.isdigit():
int(new_pin)
obj1.set_reset_pin(account_number, acctype, new_pin)
elif choice ==5:
break
else:
print("Not a valid number")
A piece of bank_account_setup:
import sqlite3
import secrets
import getpass
import smtplib, sqlite3
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import send_email
conn = sqlite3.connect('bank_account.db')
c = conn.cursor()
class BankAccount:
def __init__(self, name, social, account_number, balance, acctype):
self.name = name
self.social = social
self.account_number = account_number
self.balance = balance
self.acctype = acctype
""" create different accounts based on account type passed in """
def create_account(self, name, social, account_number, balance, acctype, card_no=None, credit_score=None, credit_limit=None):
self.rate = None
with conn:
# account_found = BankAccount.get_account(self, account_number, acctype)
# if not account_found:
if acctype == 'bank_account':
c.execute("INSERT INTO {} VALUES (:name, :social, :account_number, :balance, :pin)".format(acctype),
{'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'pin':''})
print("New account: {} has been created, acc # is: {}".format(acctype, account_number))
elif acctype == 'savings_account':
c.execute("INSERT INTO {} VALUES (:name, :social, :account_number, :balance, :rate)".format(acctype),
{'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'rate':''})
print("New account: {} has been created, acc # is: {}".format(acctype, account_number))
elif acctype == 'credit_card':
c.execute("INSERT INTO credit_card VALUES (:name, :social, :account_number, :balance, :card_no,:credit_score, :credit_limit, :pin)",
{'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'card_no'
:card_no, 'credit_score':credit_score, 'credit_limit':credit_limit, 'pin':'' })
print("New account: {} has been created, acc # is: {}".format(acctype, account_number))
conn.commit()
Piece of bank_account:
import sqlite3
import secrets
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import send_email
conn = sqlite3.connect('bank_account.db')
c = conn.cursor()
class BankAccount:
def __init__(self, name, social, account_number, balance, acctype):
self.name = name
self.social = social
self.account_number = account_number
self.balance = balance
self.acctype = acctype
def get_balance(self, account_number, acctype):
with conn:
balance = c.execute("SELECT balance from {} WHERE account_number=:account_number".format(acctype),
{'account_number':account_number})
balance = c.fetchone()
print("The balance for account number: {} is ${}".format(account_number, balance[0]))
notif_set = BankAccount.get_notif(self, account_number, acctype)
if notif_set is None:
print("No notifications are set for this user")
else:
notif_balance = notif_set[4]
name = notif_set[0]
if notif_balance == 1:
notify = send_email.send_email(account_number, acctype, 'Balance', balance, balance, name)
return(balance[0])
""" Deposit funds into the account number + acctype for the account passed in """
def deposit(self, account_number, acctype, amount):
with conn:
""" Check acct exists before making deposit """
account_found = BankAccount.get_account(self, account_number, acctype)
if account_found:
existing_bal = account_found[3]
c.execute("""UPDATE {} SET balance=balance +:amount
WHERE account_number =:account_number""".format(acctype),
{'account_number':account_number, 'amount':amount})
new_bal = existing_bal + (int(amount))
print("${} has been deposited to account {} and the new balance is ${}".format(amount, account_number, existing_bal + (int(amount))))
# Check email configurations are turned on for deposits
notif_set = BankAccount.get_notif(self, account_number, acctype)
if notif_set is None:
print("No notifications are set for this user")
else:
notif_deposits = notif_set[5]
name = notif_set[0]
if notif_deposits == 1:
notify = send_email.send_email(account_number, acctype, 'Deposit', amount, new_bal, name)
Piece of credit_card:
from bank_account import BankAccount
import sqlite3
conn = sqlite3.connect('bank_account.db')
c = conn.cursor()
class CreditCard(BankAccount):
def __init__(self, name, social, account_number, balance, acctype, card_no, credit_score=None, credit_limit=None):
super().__init__(name, social, account_number, balance, acctype)
self.card_no = card_no
self.credit_score = credit_score
self.credit_limit = credit_limit
""" set credit limit, check if acct exists, then call get credit limit """
def set_credit_limit(self, account_number, acctype, credit_score):
with conn:
account_found = BankAccount.get_account(self, account_number, acctype)
if account_found:
credit_limit = CreditCard.set_credit_limit_helper(self, account_number, credit_score)
if credit_limit:
c.execute("""UPDATE credit_card SET credit_limit=:credit_limit
WHERE account_number =:account_number """,
{'account_number':account_number, 'credit_limit':credit_limit})
print("Account number {} credit limit is set to {}".format(account_number, credit_limit))
conn.commit()
def withdraw(self, account_number, acctype, amount):
with conn:
account_found = BankAccount.get_account(self, account_number, acctype)
if account_found:
balance = account_found[3]
credit_limit = CreditCard.get_credit_limit(self, account_number)
amount_left = credit_limit - (int(balance))
if balance - (int(amount)) < credit_limit:
print("Your balance is: {}, and your credit limit is: {}".format(balance, credit_limit))
print("The max you can withdraw is {}".format(amount_left))
else:
existing_bal = account_found[3]
c.execute("""UPDATE credit_card SET balance=balance -:amount
WHERE account_number =:account_number""",
{'account_number':account_number, 'amount':amount})
print("${} has been withdrawn from account {} and the new balance is ${}".format(amount, account_number, existing_bal - (int(amount))))
notif_set = BankAccount.get_notif(self, account_number, acctype)
if notif_set is None:
print("No notifications have been set for this acct")
else:
notif_withdraw = notif_set[7]
if notif_withdraw == 1:
notify = BankAccount.send_email(self,account_number, acctype, 'Withdraw', amount, existing_bal - amount)
conn.commit()