further improvements on accounts screen

This commit is contained in:
Lucas Mathews
2024-05-26 23:23:51 +02:00
parent 739fb4d0c6
commit f1d3afb60c
5 changed files with 226 additions and 232 deletions

View File

@@ -1,8 +1,5 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Account Page
import tkinter as tk
from tkinter import messagebox, ttk
from tkinter import messagebox, ttk
import customtkinter
import json
from config import CONFIG
@@ -13,104 +10,72 @@ import sys
### Functions ###
#################
if len(sys.argv) > 3: # Check if the account description is provided as a command line argument
if len(sys.argv) > 3: # Check if the account description is provided as a command line argument
account_id = sys.argv[1]
account_description = sys.argv[3]
else:
print("Error: Account description not provided.")
sys.exit(1)
def open_account_page(account):
account_window = tk.Toplevel()
# Display the account details
account_label = tk.Label(account_window, text=f"Account ID: {account['account_id']} | Account Type: {account['account_type']} | Balance: {format_balance(account['balance'])}")
account_label.pack()
# Create buttons for making a new transaction and editing the account details
new_transaction_button = tk.Button(account_window, text="New Transaction", command=lambda: new_transaction(account))
new_transaction_button.pack()
edit_account_button = tk.Button(account_window, text="Edit Account", command=lambda: edit_account(account))
edit_account_button.pack()
transactions_frame = tk.Frame(account_window) # Create a frame for the transactions table
transactions_frame.pack(side='bottom', fill='both', expand=True)
# Create a table for the transactions
transactions_table = ttk.Treeview(transactions_frame, columns=("Transaction ID", "Amount", "Date", "Description"), show="headings")
transactions_table.heading("Transaction ID", text="Transaction ID")
transactions_table.heading("Amount", text="Amount")
transactions_table.heading("Date", text="Date")
transactions_table.heading("Description", text="Description")
transactions_table.pack(fill='both', expand=True)
populate_transactions_table(transactions_table, account) # Populate the transactions table
def populate_transactions_table(table, account):
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
account_id = account['account_id']
response = get_transactions(account_id)
def display_account_info():
response = get_account(account_id)
if response is None or 'data' not in response: # Check if the response is valid
print(f"Error: Unable to fetch account details for account {account_id}")
return
account = response['data']
print(type(account['balance'])) # Print the type of the balance
formatted_balance = format_balance(account['balance'])
account_info = (account['description'], account['account_id'], formatted_balance, account['account_type'])
return account_info
def populate_transactions_table(transactions_table, account_id):
response = get_transactions(account_id) # Fetch the transactions for the account
print(f"Response from get_transactions: {response}") # Print the response
if response is None or 'data' not in response:
print(f"Error: Unable to fetch transactions for account {account_id}")
return
transactions = response['data']
if not isinstance(transactions, list): # Check if transactions is a list
print(f"Error: Expected a list of transactions for account {account_id}, but got {type(transactions)}")
if not isinstance(transactions, list):
print(f"Error: Expected a list of transactions, but got {type(transactions)}")
return
for transaction in transactions: # Populate the table with the transactions
table.insert('', 'end', values=(transaction['transaction_id'], format_balance(transaction['amount']), transaction['date'], transaction['description']))
open_account_button = tk.Button(root, text="Open Account", command=lambda: open_account_page(get_selected_account()))
open_account_button.pack()
def get_selected_account(): # Implement this function to return the selected account
selected_item = table.selection()[0] # Get the selected item
account_id = table.item(selected_item)["values"][1] # Get the account ID from the selected item
return get_account(account_id) # Fetch the account details and return them
for transaction in transactions: # Insert the transactions into the transactions_table
transactions_table.insert('', 'end', values=(
transaction['transaction_id'],
transaction['transaction_type'],
format_balance(transaction['amount']),
transaction['timestamp'],
transaction['description'],
transaction['account_id'],
transaction['recipient_account_id']
))
##############
### Layout ###
##############
root = customtkinter.CTk()
root.title(account_description)
root.title(f"Transactions for: {account_description}")
root.iconbitmap("application/luxbank.ico")
root.geometry("800x400")
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
else:
customtkinter.set_appearance_mode("light") # Set the style for light mode
customtkinter.set_appearance_mode("light") # Set the style for light mode
welcome_label = customtkinter.CTkLabel(root, text=f"Transactions for: {account_description}", font=("Helvetica", 24))
welcome_label.pack(pady=20)
frame = customtkinter.CTkFrame(root)
table_frame = customtkinter.CTkFrame(root)
table_frame.pack(fill=tk.BOTH, expand=True)
table = ttk.Treeview(frame, columns=("Description", "Account ID", "Balance", "Account Type"), show="headings")
table.heading("Description", text="Description")
table.heading("Account ID", text="Account ID")
table.heading("Balance", text="Balance")
table.heading("Account Type", text="Account Type")
table.pack()
transactions_table = ttk.Treeview(table_frame, columns=("Transaction ID", "Transaction Type", "Amount", "Timestamp", "Description", "Account ID", "Recipient Account ID"), show="headings")
transactions_table.pack(fill=tk.BOTH, expand=True)
table.column("Description", width=200)
table.column("Account ID", width=100)
table.column("Balance", width=100)
table.column("Account Type", width=100)
for col in transactions_table["columns"]:
transactions_table.heading(col, text=col)
# Bind the double-click event to the table
table.bind("<Double-1>", lambda event: open_account_page(get_selected_account()))
# Directly populate transactions table for the given account
populate_transactions_table(transactions_table, account_id)
def get_selected_account():
selected_items = table.selection() # Get the selected items
if selected_items: # Check if any items are selected
selected_item = selected_items[0] # Get the first selected item
account_id = table.item(selected_item)["values"][1] # Get the account ID from the selected item
return get_account(account_id) # Fetch the account details and return them
else:
messagebox.showinfo("No selection", "Please select an account first.")
return None
root.mainloop()
root.mainloop()

View File

@@ -1,13 +1,26 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System App Connection file
import requests
from requests.models import PreparedRequest, Response
from requests.models import Response
from config import CONFIG
import json
##############
### System ###
##############
def format_balance(balance):
"""
Formats the balance as a currency string with comma separators.
"""
return f"{balance:,.2f}"
#####################
### API Functions ###
#####################
def authenticate_client(client_id, client_password):
"""
Authenticates a client with the given client_id and client_password.
"""
try:
response = requests.post(CONFIG["server"]["url"] + "/Client/Login", params={'client_id': client_id, 'password': client_password})
return response
@@ -19,8 +32,11 @@ def authenticate_client(client_id, client_password):
return response
def logout_client():
"""
Logs out the current client.
"""
try:
with open('application\\session_data.json', 'r') as f: # Open the session_data.json file in read mode
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.post(CONFIG["server"]["url"] + "/Client/Logout", cookies=session_data['session_cookie'])
return response
@@ -32,24 +48,26 @@ def logout_client():
return response
def get_client(client_id):
try:
with open('application\\session_data.json', 'r') as f: # Open the session_data.json file in read mode
session_data = json.load(f)
response = requests.get(CONFIG["server"]["url"] + "/Client", cookies=session_data['session_cookie'], params={'client_id': client_id})
return response.json()
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
response = Response()
response.status_code = 500
response._content = b'{"success": false, "message": "Could not connect to the server. Please try again later."}'
return response.json()
def update_client(client_id, email=None, phone_number=None, address=None):
"""
Retrieves the client details for the given client_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(CONFIG["server"]["url"] + "/Client", cookies=session_data['session_cookie'], params={'client_id': client_id})
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def update_client(client_id, email=None, phone_number=None, address=None):
"""
Updates the client details for the given client_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
# Create a dictionary of parameters to update
params = {'client_id': client_id}
if email is not None:
params['email'] = email
@@ -57,95 +75,95 @@ def update_client(client_id, email=None, phone_number=None, address=None):
params['phone_number'] = phone_number
if address is not None:
params['address'] = address
response = requests.put(
CONFIG["server"]["url"] + "/Client",
cookies=session_data['session_cookie'],
params=params
)
response.raise_for_status() # Raise an exception if the request failed
response = requests.put(CONFIG["server"]["url"] + "/Client", cookies=session_data['session_cookie'], params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def get_accounts(client_id):
"""
Retrieves the accounts associated with the given client_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(
CONFIG["server"]["url"] + "/Client/Accounts",
cookies=session_data['session_cookie'],
params={'client_id': client_id}
)
response.raise_for_status() # Raise an exception if the request failed
response = requests.get(CONFIG["server"]["url"] + "/Client/Accounts", cookies=session_data['session_cookie'], params={'client_id': client_id})
response.raise_for_status()
accounts = response.json()
if isinstance(accounts, str): # If the response is a string, convert it to a list of dictionaries
if isinstance(accounts, str):
accounts = json.loads(accounts)
return accounts
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def format_balance(balance): # Formats the balance as a currency string with comma seperator
return f"{balance:,.2f}"
def get_transactions(account_id):
"""
Retrieves the transactions for the given account_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(
CONFIG["server"]["url"] + "/Account/Transactions",
cookies=session_data['session_cookie'],
params={'account_id': account_id}
)
response.raise_for_status() # Raise an exception if the request failed
response = requests.get(CONFIG["server"]["url"] + "/Transaction/History", cookies=session_data['session_cookie'], params={'account_id': account_id})
response.raise_for_status()
transactions = response.json()
if isinstance(transactions, str): # If the response is a string, convert it to a list of dictionaries
if isinstance(transactions, str):
transactions = json.loads(transactions)
return transactions
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def get_account(account_id):
"""
Retrieves the account details for the given account_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(
CONFIG["server"]["url"] + "/Account",
cookies=session_data['session_cookie'],
params={'account_id': account_id}
)
response.raise_for_status() # Raise an exception if the request failed
return response.json()
response = requests.get(CONFIG["server"]["url"] + "/Account", cookies=session_data['session_cookie'], params={'account_id': account_id})
response.raise_for_status()
account = response.json()
if 'data' not in account or 'balance' not in account['data']:
print(f"Error: The account dictionary does not have a 'balance' key. Account: {account}")
return None
return account
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def update_account(account_id, description=None, notes=None):
"""
Updates the account details for the given account_id.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
# Create a dictionary of parameters to update
params = {'account_id': account_id}
if description is not None:
params['description'] = description
if notes is not None:
params['notes'] = notes
response = requests.put(
CONFIG["server"]["url"] + "/Account",
cookies=session_data['session_cookie'],
params=params
)
response.raise_for_status() # Raise an exception if the request failed
response = requests.put(CONFIG["server"]["url"] + "/Account", cookies=session_data['session_cookie'], params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def new_transaction(account):
"""
Creates a new transaction for the given account.
"""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
params = {'account_id': account['account_id']}
response = requests.post(CONFIG["server"]["url"] + "/Transaction/History", cookies=session_data['session_cookie'], params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}

View File

@@ -1,14 +1,11 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Dashboard Page
import tkinter as tk
from tkinter import messagebox, ttk
from tkinter import messagebox, ttk
import customtkinter
import json
from config import CONFIG
import os
from config import CONFIG
from connection import logout_client, get_client, update_client, get_accounts, format_balance
# Global variables
email_entry = None
phone_entry = None
address_entry = None
@@ -19,55 +16,56 @@ frame = None
#################
def logout():
response = logout_client() # Call the logout_client function
json_response = response.json() # Convert the response content to JSON
if json_response['success'] == True:
"""Logs out the client and closes the application."""
response = logout_client()
json_response = response.json()
if json_response['success']:
messagebox.showinfo("Logout", "You have been logged out.")
root.destroy()
else:
messagebox.showerror("Logout failed", json_response['message'])
def display_client_info():
global frame # Declare frame as global inside the function
"""Displays the client's information on the dashboard."""
global frame
if frame is not None:
for widget in frame.winfo_children(): # Destroy all widgets in the frame
for widget in frame.winfo_children():
widget.destroy()
else:
frame = customtkinter.CTkFrame(root)
frame.pack(anchor='w', side='left', padx=20, pady=20)
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
client_info = get_client(client_id)
if 'success' in client_info and client_info['success']:
client = client_info['data']
fields = [('Name', 'name'),
('Client ID', 'client_id'),
('Email', 'email'),
('Phone', 'phone_number'),
('Address', 'address'),
('Account Opened', 'opening_timestamp')]
for i, (display_name, key) in enumerate(fields):
value = client.get(key, 'N/A') # Use 'N/A' as the default value if the key is not found
label_key = customtkinter.CTkLabel(frame, text=f"{display_name}: ", font=("Helvetica", 14))
label_value = customtkinter.CTkLabel(frame, text=value, font=("Helvetica", 14))
label_key.grid(row=i, column=0, sticky='e')
label_value.grid(row=i, column=1, sticky='w')
else:
error_label = customtkinter.CTkLabel(root, text="Error: Could not retrieve client information", font=("Helvetica", 14))
error_label.pack(pady=20)
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
client_info = get_client(client_id)
if client_info.get('success'):
client = client_info['data']
fields = [('Name', 'name'), ('Client ID', 'client_id'), ('Email', 'email'), ('Phone', 'phone_number'),
('Address', 'address'), ('Account Opened', 'opening_timestamp')]
for i, (display_name, key) in enumerate(fields):
value = client.get(key, 'N/A')
label_key = customtkinter.CTkLabel(frame, text=f"{display_name}: ", font=("Helvetica", 14))
label_value = customtkinter.CTkLabel(frame, text=value, font=("Helvetica", 14))
label_key.grid(row=i, column=0, sticky='e')
label_value.grid(row=i, column=1, sticky='w')
else:
error_label = customtkinter.CTkLabel(root, text="Error: Could not retrieve client information", font=("Helvetica", 14))
error_label.pack(pady=20)
except Exception as e:
messagebox.showerror("Error", f"Could not retrieve client information: {e}")
edit_button = customtkinter.CTkButton(frame, text="Edit Details", command=edit_details)
edit_button.grid(row=len(fields), column=0, columnspan=2)
def edit_details():
global edit_window, email_entry, phone_entry, address_entry # Declare the variables as global inside the function
"""Opens a new window for editing client details."""
global edit_window, email_entry, phone_entry, address_entry
edit_window = customtkinter.CTkToplevel(root)
edit_window.title("Edit Details")
edit_window.geometry("300x200")
edit_window.attributes('-topmost', True)
email_label = customtkinter.CTkLabel(edit_window, text="Email: ")
email_entry = customtkinter.CTkEntry(edit_window)
@@ -89,57 +87,68 @@ def edit_details():
edit_window.lift()
def save_details():
"""Saves the updated client details."""
global edit_window, email_entry, phone_entry, address_entry
new_email = email_entry.get() if email_entry.get() != '' else None
new_phone = phone_entry.get() if phone_entry.get() != '' else None
new_address = address_entry.get() if address_entry.get() != '' else None
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
if not messagebox.askyesno("Confirmation", "Are you sure you want to update the details?"):
return # If the user clicked 'No', exit the function
result = update_client(client_id, new_email, new_phone, new_address)
if result['success']:
display_client_info()
edit_window.destroy()
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
if not messagebox.askyesno("Confirmation", "Are you sure you want to update the details?"):
return
result = update_client(client_id, new_email, new_phone, new_address)
if result['success']:
display_client_info()
else:
messagebox.showerror("Update Failed", result.get('message', 'Unknown error'))
edit_window.destroy()
except Exception as e:
messagebox.showerror("Error", f"Could not update details: {e}")
def populate_table():
with open('application\\session_data.json', 'r') as f: # Get the client_id from the session data
session_data = json.load(f)
client_id = session_data['client_id']
response = get_accounts(client_id) # Get the accounts for the client
accounts = response['data'] if 'data' in response else []
if not isinstance(accounts, list): # Check if accounts is a list
print(f"Error: Expected a list of accounts, but got {type(accounts)}")
return
for account in accounts: # Populate the table with the accounts
formatted_balance = format_balance(account['balance'])
table.insert('', 'end', values=(account['description'], account['account_id'], formatted_balance, account['account_type']))
"""Populates the accounts table with client accounts."""
try:
with open('application\\session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
response = get_accounts(client_id)
accounts = response.get('data', [])
if not isinstance(accounts, list):
raise ValueError(f"Expected a list of accounts, but got {type(accounts)}")
for account in accounts:
formatted_balance = format_balance(account['balance'])
table.insert('', 'end', values=(account['description'], account['account_id'], formatted_balance, account['account_type']))
except Exception as e:
messagebox.showerror("Error", f"Could not populate accounts table: {e}")
def on_account_double_click(event):
"""Handles double-click event on an account in the table."""
try:
selected_account = table.item(table.selection()) # Get the selected account
session = json.load(open('application\\session_data.json', 'r')) # Get the session data
selected_account = table.item(table.selection())
session = json.load(open('application\\session_data.json', 'r'))
account_description = selected_account['values'][0]
os.system(f"python application\\account.py {selected_account['values'][1]} {session['client_id']} \"{account_description}\"")
command = f"python application\\account.py {selected_account['values'][1]} {session['client_id']} \"{account_description}\""
return_code = os.system(command)
if return_code != 0:
print(f"Error: The command failed with return code {return_code}")
except Exception as e:
print(f"Error: {e}")
##############
### Layout ###
##############
root = customtkinter.CTk()
root.title("Luxbank Dashboard")
root.iconbitmap("application/luxbank.ico")
root.geometry("800x350")
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
else:
customtkinter.set_appearance_mode("light") # Set the style for light mode
# Set appearance mode based on configuration
customtkinter.set_appearance_mode(CONFIG["preferences"].get("dark_theme", "light"))
# Create a label for the title
welcome_label = customtkinter.CTkLabel(root, text="Welcome to the Luxbank Dashboard!", font=("Helvetica", 24))
@@ -171,11 +180,6 @@ table.pack(fill='both', expand=True)
populate_table()
# Create a scrollbar for the table
scrollbar = ttk.Scrollbar(table_frame, orient='vertical', command=table.yview)
scrollbar.pack(side='right', fill='y')
table.configure(yscrollcommand=scrollbar.set)
table.bind("<Double-1>", on_account_double_click)
root.mainloop()
root.mainloop()

View File

@@ -1,6 +1,4 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System App Login Page
import tkinter as tk
from tkinter import messagebox
import customtkinter
import os
@@ -14,55 +12,64 @@ from config import CONFIG
#################
def login():
"""Function to handle the login process."""
client_id = entry_username.get() if entry_username.get() else CONFIG["client"]["default_id"]
client_password = entry_password.get() if entry_password.get() else CONFIG["client"]["default_password"]
try:
response = authenticate_client(client_id, client_password) # Authenticate the client
json_response = response.json() # Convert the response content to JSON
if json_response["success"] == True: # If the authentication is successful, open the dashboard
response = authenticate_client(client_id, client_password) # Authenticate the client
json_response = response.json() # Convert the response content to JSON
if json_response["success"]: # If the authentication is successful, open the dashboard
session_data = {
'session_cookie': response.cookies.get_dict(),
'client_id': client_id
}
with open('application\\session_data.json', 'w') as f: # Save the session data to a file
with open('application/session_data.json', 'w') as f: # Save the session data to a file
json.dump(session_data, f)
root.destroy()
os.system("python application\\dashboard.py")
os.system("python application/dashboard.py")
else:
messagebox.showerror("Login failed", json_response["message"])
except requests.exceptions.RequestException as e:
messagebox.showerror("Login failed", "Could not connect to the server. Please try again later. Error: " + str(e))
messagebox.showerror("Login failed", f"Could not connect to the server. Please try again later. Error: {str(e)}")
##############
### Layout ###
##############
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
# Set appearance mode based on configuration
if CONFIG["preferences"]["dark_theme"] == "dark":
customtkinter.set_appearance_mode("dark")
else:
customtkinter.set_appearance_mode("light") # Set the style for light mode
customtkinter.set_appearance_mode("light")
# Initialize the main window
root = customtkinter.CTk()
root.title("Luxbank Login")
root.iconbitmap("application/luxbank.ico")
root.geometry("400x300")
# Create and pack the label for the title
label = customtkinter.CTkLabel(root, text="Luxbank", font=("Helvetica", 24))
label.pack(pady=20)
# Create and pack the username entry
entry_username = customtkinter.CTkEntry(root, placeholder_text="Client ID")
entry_username.pack(pady=20)
# Create and pack the password entry
entry_password = customtkinter.CTkEntry(root, placeholder_text="Password", show="*")
entry_password.pack(pady=10)
login_button= customtkinter.CTkButton(root, text="Login", command=login)
# Create and pack the login button
login_button = customtkinter.CTkButton(root, text="Login", command=login)
login_button.pack(pady=15)
# Bind the Return key to the login function
root.bind('<Return>', lambda event=None: login())
###########
### Run ###
###########
root.mainloop()
# Start the main event loop
root.mainloop()

View File

@@ -1 +1 @@
{"session_cookie": {"session": "a7Gs_67Dz2cXT6kbk0IMxupSkpxND5x7PIdiPK4RJgA"}, "client_id": "31d90aad"}
{"session_cookie": {"session": "49JGnuF82p0Ed1I5QnnyXirNCJOY1g2EG7D5dO8C96I"}, "client_id": "31d90aad"}