diff --git a/application/account.py b/application/account.py index 6a5cc0e..d87fb3c 100644 --- a/application/account.py +++ b/application/account.py @@ -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("", 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() \ No newline at end of file +root.mainloop() diff --git a/application/connection.py b/application/connection.py index 48e39c9..1c34d28 100644 --- a/application/connection.py +++ b/application/connection.py @@ -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."} - diff --git a/application/dashboard.py b/application/dashboard.py index bab5f21..f97f376 100644 --- a/application/dashboard.py +++ b/application/dashboard.py @@ -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("", on_account_double_click) -root.mainloop() \ No newline at end of file +root.mainloop() diff --git a/application/login.py b/application/login.py index cad844a..dfb07a0 100644 --- a/application/login.py +++ b/application/login.py @@ -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('', lambda event=None: login()) ########### ### Run ### ########### -root.mainloop() \ No newline at end of file +# Start the main event loop +root.mainloop() diff --git a/application/session_data.json b/application/session_data.json index c4f9573..6f215f4 100644 --- a/application/session_data.json +++ b/application/session_data.json @@ -1 +1 @@ -{"session_cookie": {"session": "a7Gs_67Dz2cXT6kbk0IMxupSkpxND5x7PIdiPK4RJgA"}, "client_id": "31d90aad"} \ No newline at end of file +{"session_cookie": {"session": "49JGnuF82p0Ed1I5QnnyXirNCJOY1g2EG7D5dO8C96I"}, "client_id": "31d90aad"} \ No newline at end of file