In this assignment, you will implement an online banking system. Users can sign up with the system, log in to the system, change their password, and delete their account. They can also update their bank account balance and transfer money to another user’s bank account.
You’ll implement functions related to File I/O and dictionaries. The first two functions require you to import files and create dictionaries. User information will be imported from the “users.txt” file and account information will be imported from the “bank.txt” file. Take a look at the content in the different files. The remaining functions require you to use or modify the two dictionaries created from the files.
Each function has been defined for you, but without the code. See the docstring in each function for instructions on what the function is supposed to do and how to write the code. It should be clear enough. In some cases, we have provided hints to help you get started.
###########################################################
### EXECUTE THIS CELL BEFORE YOU TO TEST YOUR SOLUTIONS ###
###########################################################
import nose.tools as tools
def isfloat(x):
try:
float(x)
return True
except:
return False
def create_list(line, delimiter):
# split line into a list
# trim white spaces
# skip invalid line
lst=line.strip().split(delimiter)
if(len(lst)!=2):
return False
lst[0]=lst[0].strip()
lst[1]=lst[1].strip()
return lst
def create_bank(bank,username,amount):
if isfloat(amount):
value = bank.get(username,0) + float(amount)
if(value >=0):
bank.update({username:value})
return (username in bank)
return False
return False
def import_and_create_bank(filename):
'''
This function is used to create a bank dictionary. The given argument is the filename to load.
Every line in the file should be in the following format:
key: value
The key is a user's name and the value is an amount to update the user's bank account with. The value should be a
number, however, it is possible that there is no value or that the value is an invalid number.
What you will do:
- Create an empty bank dictionary.
- Read in the file.
- Add keys and values to the dictionary from the contents of the file.
- If the key doesn't exist in the dictionary, create a new key:value pair.
- If the key does exist in the dictionary, increment its value with the amount.
- You should also handle the following cases:
-- When the value is missing or invalid. If so, ignore that line and don't update the dictionary.
-- When the line is completely blank. Again, ignore that line and don't update the dictionary.
-- When there is whitespace at the beginning or end of a line and/or between the name and value on a line. You
should trim any and all whitespace.
- Return the bank dictionary from this function.
For example, here's how your code should handle some specific lines in the file:
The 1st line in the file has a name and valid number:
Brandon: 5
Your code will process this line and add the extracted information to the dictionary. After it does,
the dictionary will look like this:
bank = {"Brandon": 5}
The 2nd line in the file also has a name and valid number:
Patrick: 18.9
Your code will also process this line and add the extracted information to the dictionary. After it does,
the dictionary will look like this:
bank = {"Brandon": 5, "Patrick": 18.9}
The 3rd line in the file has a name but invalid number:
Brandon: xyz
Your code will ignore this line and add nothing to the dictionary. It will still look like this:
bank = {"Brandon": 5, "Patrick": 18.9}
The 4th line in the file has a name but missing number:
Jack:
Your code will ignore this line and add nothing to the dictionary. It will still look like this:
bank = {"Brandon": 5, "Patrick": 18.9}
The 5th line in the file is completely blank.
Your code will ignore this line and add nothing to the dictionary. It will still look like this:
bank = {"Brandon": 5, "Patrick": 18.9}
The 8th line in the file has a name and valid number, but with extra whitespace:
Brandon: 10
Your code will process this line and update the value associated with the existing key ('Brandon') in the dictionary.
After it does, the value associated with the key 'Brandon' will be 10:
bank = {"Brandon": 15, ...}
After processing every line in the file, the dictionary will look like this:
bank = {"Brandon": 115.5, "Patrick": 18.9, "Sarah": 827.43, "Jack": 45.0, "James": 128.87}
Return the dictionary from this function.
'''
bank = {}
with open(filename) as file_handle:
for line in file_handle:
lst = create_list(line, ":")
if(not lst): continue
create_bank(bank,lst[0],lst[1])
"""
if isfloat(lst[1]):
value = bank.get(lst[0],0) + float(lst[1].strip())
bank.update({lst[0]:value})
"""
return bank
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
tools.assert_false(len(bank) == 0, "The bank dictionary is not supposed to be empty after reading 'bank.txt'.")
tools.assert_almost_equal(115.5, bank.get("Brandon"), msg="The total amount for Brandon is incorrect. It should be 115.5.")
tools.assert_almost_equal(128.87, bank.get("James"), msg="The total amount for James is incorrect. It should be 128.87.")
tools.assert_is_none(bank.get("Joel"), "Joel should not be in the bank dictionary.")
tools.assert_is_none(bank.get("Luke"), "Luke should not be in the bank dictionary.")
tools.assert_almost_equal(827.43, bank.get("Sarah"), msg="The total amount for Sarah is incorrect. It should be 827.43.")
print("Success!")
Success!
def isvalid_password(password):
#bit positions (00001111): 0000 ,length, lowercase, uppercase, digit
if(len(password)<8): return False
validator = 8 # when (0 bitWiseOr 8) => 00001000==8
for letter in password:
if(validator==15): break
letter_ascii= ord(letter)
if(letter_ascii > 47 and letter_ascii < 58 ): validator = (validator | 1) #digit range, mask_bits: 0001==1
elif(letter_ascii >64 and letter_ascii < 91): validator = (validator | 2) #uppercase letters, mask_bits: 0010==2
elif(letter_ascii >96 and letter_ascii <123): validator = (validator | 4) #lowercase letter, mask_bits: 0100==4
return (validator==15) # 00001111==15
def isvalid_user(user, user_accounts):
return (not user in user_accounts)
def validate( user_accounts, user, password):
if isvalid_password(password) and isvalid_user(user, user_accounts) and (user != password):
return True
return False
def signup(user_accounts, log_in, username, password):
'''
This function allows users to sign up.
If both username and password meet the requirements:
- Updates the username and the corresponding password in the user_accounts dictionary.
- Updates the log_in dictionary, setting the value to False.
- Returns True.
If the username and password fail to meet any one of the following requirements, returns False.
- The username already exists in the user_accounts.
- The password must be at least 8 characters.
- The password must contain at least one lowercase character.
- The password must contain at least one uppercase character.
- The password must contain at least one number.
- The username & password cannot be the same.
For example:
- Calling signup(user_accounts, log_in, "Brandon", "123abcABCD") will return False
- Calling signup(user_accounts, log_in, "BrandonK", "123ABCD") will return False
- Calling signup(user_accounts, log_in, "BrandonK","abcdABCD") will return False
- Calling signup(user_accounts, log_in, "BrandonK", "123aABCD") will return True. Then calling
signup(user_accounts, log_in, "BrandonK", "123aABCD") again will return False.
Hint: Think about defining and using a separate valid(password) function that checks the validity of a given password.
This will also come in handy when writing the change_password() function.
'''
# if username in user_accounts:
# print("DDD")
# return False
if not validate( user_accounts, username, password): return False
if validate( user_accounts, username, password):
value = password.strip()
key = username.strip()
user_accounts.update({key:value})
log_in.update({key:False})
if(user_accounts is None): return False
return user_accounts, log_in
def import_and_create_accounts(filename):
'''
This function is used to create an user accounts dictionary and another login dictionary. The given argument is the
filename to load.
Every line in the file should be in the following format:
username - password
The key is a username and the value is a password. If the username and password fulfills the requirements,
add the username and password into the user accounts dictionary. To make sure that the password fulfills these
requirements, be sure to use the signup function that you wrote above.
For the login dictionary, the key is the username, and its value indicates whether the user is logged in, or not.
Initially, all users are not logged in.
What you will do:
- Create an empty user accounts dictionary and an empty login dictionary.
- Read in the file.
- If the username and password fulfills the requirements, adds the username and password
into the user accounts dictionary, and updates the login dictionary.
- You should also handle the following cases:
-- When the password is missing. If so, ignore that line and don't update the dictionaries.
-- When there is whitespace at the beginning or end of a line and/or between the name and password on a line. You
should trim any and all whitespace.
- Return both the user accounts dictionary and login dictionary from this function.
For example, here's how your code should handle some specific lines in the file:
The 1st line in the file has a name and password:
Brandon - brandon123ABC
Your code will process this line, and using the signup function, will add the extracted information to the
dictionaries. After it does, the dictionaries will look like this:
user_accounts = {"Brandon": "brandon123ABC"}
log_in = {"Brandon": False}
The 2nd line in the file has a name but missing password:
Jack
Your code will ignore this line. The dictionaries will still look like this:
user_accounts = {"Brandon": "brandon123ABC"}
log_in = {"Brandon": False}
The 3rd line in the file has a name and password:
Jack - jac123
Your code will process this line, and using the signup function, will not add the extracted information to the
dictionaries because the password is invalid. The dictionaries will still look like this:
user_accounts = {"Brandon": "brandon123ABC"}
log_in = {"Brandon": False}
The 4th line in the file has a name and password:
Jack - jack123POU
Your code will process this line, and using the signup function, will add the extracted information to the
dictionaries. After it does, the dictionaries will look like this:
user_accounts = {"Brandon": "brandon123ABC, "Jack": "jack123POU"}
log_in = {"Brandon": False, "Jack": False}
After processing every line in the file, the dictionaries will look like this:
user_accounts = {"Brandon": "brandon123ABC, "Jack": "jack123POU", "James": "100jamesABD", "Sarah": "sd896ssfJJH"}
log_in = {"Brandon": False, "Jack": False, "James": False, "Sarah": False}
Return the dictionaries from this function.
'''
user_accounts = {}
log_in = {}
with open(filename) as file_handle:
for line in file_handle:
lst = create_list(line, "-")
if(not lst): continue
result =signup(user_accounts, log_in, lst[0], lst[1])
if(result):
user_accounts, log_in =result
if(user_accounts is None): return False
return user_accounts,log_in
##########################
### TEST YOUR SOLUTION ###
##########################
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(len(user_accounts) == 0, "The user_accounts dictionary is not supposed to be empty after reading 'user.txt'." )
tools.assert_false(len(log_in) == 0, "The login dictionary is not supposed to be empty after reading 'user.txt'.")
tools.assert_equal("brandon123ABC", user_accounts.get("Brandon"), "The password associated with username 'Brandon' is incorrect.")
tools.assert_equal("jack123POU", user_accounts.get("Jack"),"The password associated with username 'Jack' is incorrect.")
tools.assert_is_none(user_accounts.get("Jennie"), "Jennie should not appear in user_accounts, since the associated password was invalid.")
tools.assert_false(log_in["Sarah"], "'Sarah' has not logged in yet, so this should be false initially.")
print("Success!")
Success!
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(signup(user_accounts,log_in,"Brandon","123abcABCD"), "When signing up, if the username already exists in user_accounts, return False.")
tools.assert_false(signup(user_accounts,log_in,"BrandonK","12abCD"), "When signing up, if the password does not have at least 8 characters, return False.")
tools.assert_false(signup(user_accounts,log_in,"BrandonK","1234ABCD"), "When signing up, if the password does not have at least one lowercase character, return False.")
tools.assert_false(signup(user_accounts,log_in,"BrandonK","abcdABCD"), "When signing up, if the password does not have at least one number, return False.")
tools.assert_false(signup(user_accounts,log_in,"BrandonK","1234abcd"), "When signing up, if the password does not have at least one uppercase character, return False.")
tools.assert_false(signup(user_accounts,log_in,"123abcABCD","123abcABCD"), "When signing up, if the username & password are the same, return False.")
tools.assert_true(signup(user_accounts,log_in,"BrandonK","123aABCD"), "The user should be able to sign up with username 'BrandonK' and password '123aABCD'.")
tools.assert_false(signup(user_accounts,log_in,"BrandonK","123aABCD"), "Since 'BrandonK' was able to sign up already, 'BrandonK' should not be able to signup again.")
tools.assert_true("BrandonK" in user_accounts, "BrandonK is not in user_accounts.")
tools.assert_equal("123aABCD",user_accounts["BrandonK"], "The password associated with'BrandonK' is incorrect." )
tools.assert_false(log_in["BrandonK"], "'BrandonK' has not logged in yet, so this should be false initially.")
print("Success!")
Success!
def is_auth(user_accounts,username,password):
return user_accounts.get(username)==password
def login(user_accounts, log_in, username, password):
'''
This function allows users to log in with their username and password.
The user_accounts dictionary stores the username and associated password.
The log_in dictionary stores the username and associated log-in status.
If the username does not exist in user_accounts or the password is incorrect:
- Returns False.
Otherwise:
- Updates the user's log-in status in the log_in dictionary, setting the value to True.
- Returns True.
For example:
- Calling login(user_accounts, "Brandon", "123abcAB") will return False
- Calling login(user_accounts, "Brandon", "brandon123ABC") will return True
'''
# your code here
if username in user_accounts:
result =is_auth(user_accounts,username,password)
log_in.update({username:result})
return result
return False
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(login(user_accounts, log_in,"Brandon","123abcAB"), "If the password is incorrect, the user should not be able to log in.")
tools.assert_true(login(user_accounts, log_in,"Brandon","brandon123ABC"), "If the password is correct, the user should be able to log in.")
tools.assert_false(login(user_accounts, log_in,"BrandonK","123abcABC"), "If the user is not in user_accounts, return False.")
print("Success!")
Success!
def islogin(username,log_in):
return log_in.get(username,False)
def update(bank, log_in, username, amount):
'''
In this function, you will try to update the given user's bank account with the given amount.
bank is a dictionary where the key is the username and the value is the user's account balance.
log_in is a dictionary where the key is the username and the value is the user's log-in status.
amount is the amount to update with, and can either be positive or negative.
To update the user's account with the amount, the following requirements must be met:
- The user exists in log_in and his/her status is True, meaning, the user is logged in.
If the user doesn't exist in the bank, create the user.
- The given amount can not result in a negative balance in the bank account.
Return True if the user's account was updated.
For example, if Brandon has 115.50 in his account:
- Calling update(bank, log_in, "Brandon", 50) will return False, unless "Brandon" is first logged in. Then it
will return True. Brandon will then have 165.50 in his account.
- Calling update(bank, log_in, "Brandon", -200) will return False because Brandon does not have enough in his
account.
'''
if islogin(username,log_in): return create_bank(bank,username,amount)
return False
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(update(bank,log_in,"Jack",100), "When the user is not logged in, return False." )
login(user_accounts, log_in, "Brandon", "brandon123ABC")
True
tools.assert_false(update(bank,log_in,"Brandon",-400), "When the user does not have enough money in the account, return False.")
tools.assert_true(update(bank,log_in,"Brandon",100), "'Brandon' should be able to increase the amount in the account by 100.")
tools.assert_almost_equal(bank.get("Brandon"),215.5, msg="After the update, the total amount for Brandon is incorrect. It should be 215.5.")
signup(user_accounts, log_in, "BrandonK", "123aABCD")
({'Brandon': 'brandon123ABC', 'Jack': 'jack123POU', 'James': '100jamesABD', 'Sarah': 'sd896ssfJJH', 'BrandonK': '123aABCD'}, {'Brandon': True, 'Jack': False, 'James': False, 'Sarah': False, 'BrandonK': False})
tools.assert_is_none(bank.get("BrandonK"), "'BrandonK' should not be in the bank dictionary yet.")
login(user_accounts,log_in,"BrandonK","123aABCD")
True
tools.assert_true(update(bank,log_in,"BrandonK", 100), "'BrandonK' should be able to increase the amount in the account by 100.")
tools.assert_almost_equal(100, bank.get("BrandonK"), msg="After the update, the total amount for 'BrandonK' is incorrect. It should be 100.")
print("Success!")
Success!
def balance(bank,username):
return bank.get(username,0)
def transfer(bank, log_in, userA, userB, amount):
'''
In this function, you will try to make a transfer between two user accounts.
bank is a dictionary where the key is the username and the value is the user's account balance.
log_in is a dictionary where the key is the username and the value is the user's log-in status.
amount is the amount to be transferred between user accounts (userA and userB). amount is always positive.
What you will do:
- Deduct the given amount from userA and add it to userB, which makes a transfer.
- You should consider some following cases:
- userA must be in the bank and his/her log-in status in log_in must be True.
- userB must be in log_in, regardless of log-in status. userB can be absent in the bank.
- No user can have a negative amount in their account. He/she must have a positive or zero balance.
Return True if a transfer is made.
For example:
- Calling transfer(bank, log_in, "BrandonK", "Jack", 100) will return False
- Calling transfer(bank, log_in, "Brandon", "JackC", 100) will return False
- After logging "Brandon" in, calling transfer(bank, log_in, "Brandon", "Jack", 10) will return True
- Calling transfer(bank, log_in, "Brandon", "Jack", 200) will return False
'''
if not userA in bank or not userA in log_in: return False
if not userB in log_in: return False
if not islogin(userA,log_in): return False
if (balance(bank,userA)-amount >=0):
print(update(bank, log_in, userA, -amount),"Dr")
print(create_bank(bank,userB,amount),"Cr")
return True
return False
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(transfer(bank,log_in,"BrandonK","Jack",100), "'BrandonK' is not in the bank or log_in dictionaries yet, so this should return False.")
tools.assert_false(transfer(bank,log_in,"Brandon","JackC",100), "'JackC is not in log_in, so this should return false.")
tools.assert_false(transfer(bank,log_in,"Brandon","Jack",100), "'Brandon' should be logged in to transfer money, so this should return False.")
login(user_accounts,log_in,"Brandon","brandon123ABC")
True
tools.assert_false(transfer(bank,log_in,"Brandon","Jack",200), "'Brandon' does not have enough money to transfer to 'Jack', so this should return False.")
tools.assert_true(transfer(bank,log_in,"Brandon","Jack",10), "'Brandon' does have enough money to transfer to 'Jack', so this should return True.")
True Dr
True Cr
tools.assert_almost_equal(105.5, bank.get("Brandon"), msg="After the transfer, the total amount for 'Brandon' is incorrect. It should be 105.5.")
tools.assert_almost_equal(55, bank.get("Jack"), msg="After the transfer, the total amount for 'Jack' is incorrect. It should be 55.")
signup(user_accounts,log_in,"BrandonK","123aABCD")
({'Brandon': 'brandon123ABC', 'Jack': 'jack123POU', 'James': '100jamesABD', 'Sarah': 'sd896ssfJJH', 'BrandonK': '123aABCD'}, {'Brandon': True, 'Jack': False, 'James': False, 'Sarah': False, 'BrandonK': False})
tools.assert_is_none(bank.get("BrandonK"), "'BrandonK' should not be in the bank dictionary yet.")
login(user_accounts,log_in,"BrandonK","123aABCD")
True
tools.assert_true(transfer(bank,log_in,"Brandon","BrandonK",10), "'Brandon' does have enough money to transfer to 'BrandonK', so this should return True.")
True Dr
True Cr
tools.assert_almost_equal(bank.get("Brandon"),95.5, msg="After the transfer, the total amount for 'Brandon' is incorrect. It should be 95.5.")
tools.assert_almost_equal(bank.get("BrandonK"),10, msg="After the transfer, the total amount for 'BrandonK' is incorrect. It should be 10.")
print("Success!")
Success!
def change_password(user_accounts, log_in, username, old_password, new_password):
'''
This function allows users to change their password.
If all of the following requirements are met, changes the password and returns True. Otherwise, returns False.
- The username exists in the user_accounts.
- The user is logged in (the username is associated with the value True in the log_in dictionary)
- The old_password is the user's current password.
- The new_password should be different from the old one.
- The new_password fulfills the requirement in signup.
For example:
- Calling change_password(user_accounts, log_in, "BrandonK", "123abcABC" ,"123abcABCD") will return False
- Calling change_password(user_accounts, log_in, "Brandon", "123abcABCD", "123abcABCDE") will return False
- Calling change_password(user_accounts, log_in, "Brandon", "brandon123ABC", "brandon123ABC") will return False
- Calling change_password(user_accounts, log_in, "Brandon", "brandon123ABC", c"123abcABCD") will return True
Hint: Think about defining and using a separate valid(password) function that checks the validity of a given password.
This will also come in handy when writing the signup() function.
'''
if not islogin(username,log_in): return False
if not old_password !=new_password: return False
if not isvalid_password(new_password): return False
if not is_auth(user_accounts,username,old_password): return False
if not user_accounts[username]==old_password: return False
user_accounts.update({username:new_password})
return True
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(change_password(user_accounts,log_in,"BrandonK","123abcABC","123abcABCD"), "BrandonK is not in user_accounts yet, so this should return False.")
tools.assert_false(change_password(user_accounts,log_in,"Brandon","brandon123ABC","123abcABCD"), "A user must be logged in to change the password.")
login(user_accounts,log_in,"Brandon","brandon123ABC")
True
tools.assert_false(change_password(user_accounts,log_in,"Brandon","123abcABCD","123abcABCDE"), "The old password entered should be the same as the current password.")
tools.assert_false(change_password(user_accounts,log_in,"Brandon","brandon123ABC","brandon123ABC"), "The new password should be different from the old one.")
tools.assert_false(change_password(user_accounts,log_in,"Brandon","brandon123ABC","123ABCD"), "The new password should be valid.")
tools.assert_true(change_password(user_accounts,log_in,"Brandon","brandon123ABC","123abcABCD"), "This function should return True when there are no issues.")
tools.assert_equal("123abcABCD",user_accounts["Brandon"], "The password associated with 'Brandon' is incorrect.")
print("Success!")
Success!
def delete_account(user_accounts, log_in, bank, username, password):
'''
Completely deletes the user from the online banking system.
If the user exists in the user_accounts dictionary and the password is correct, and the user
is logged in (the username is associated with the value True in the log_in dictionary):
- Deletes the user from the user_accounts dictionary, the log_in dictionary, and the bank dictionary.
- Returns True.
Otherwise:
- Returns False.
For example:
- Calling delete_account(user_accounts, log_in, bank, "BrandonK", "123abcABC") will return False
- Calling delete_account(user_accounts, log_in, bank, "Brandon", "123abcABDC") will return False
- If you first log "Brandon" in, calling delete_account(user_accounts, log_in, bank, "Brandon", "brandon123ABC")
will return True
'''
if islogin(username,log_in) and is_auth(user_accounts,username,password):
user_accounts.pop(username)
log_in.pop(username)
bank.pop(username)
"""
del user_accounts[username]
del log_in[username]
del bank[username]
"""
return True
return False
##########################
### TEST YOUR SOLUTION ###
##########################
bank = import_and_create_bank("bank.txt")
user_accounts, log_in = import_and_create_accounts("user.txt")
tools.assert_false(delete_account(user_accounts,log_in,bank,"BrandonK","123abcABC"), "'BrandonK' is not in user_accounts yet, so this should return False.")
tools.assert_false(delete_account(user_accounts,log_in,bank,"Brandon","brandon123ABC"), "'Brandon' is not logged in yet, so this should return False.")
login(user_accounts,log_in,"Brandon","brandon123ABC")
True
tools.assert_false(delete_account(user_accounts,log_in,bank,"Brandon","123abcABDC"), "The password is incorrect, so this should return False." )
tools.assert_true(delete_account(user_accounts,log_in,bank,"Brandon","brandon123ABC"),"'Brandon' should be able to delete the account when there are no issues.")
tools.assert_is_none(user_accounts.get("Brandon"), "After deleting the account, 'Brandon' should not be in user_accounts.")
tools.assert_is_none(log_in.get("Brandon"), "After deleting the account, 'Brandon' should not be in log_in.")
tools.assert_is_none(bank.get("Brandon"), "After deleting the account, 'Brandon' should not be in bank.")
print("Success!")
Success!