| import streamlit as st |
| import pandas as pd |
| import matplotlib.pyplot as plt |
| from datetime import date, datetime |
| import numpy as np |
| import os |
| import json |
|
|
| |
| CREDENTIALS_FILE = "user_credentials.json" |
|
|
| |
| def initialize_credentials_file(): |
| if not os.path.exists(CREDENTIALS_FILE): |
| with open(CREDENTIALS_FILE, "w") as f: |
| json.dump({}, f) |
|
|
| |
| def save_user_credentials(username, password): |
| with open(CREDENTIALS_FILE, "r") as f: |
| credentials = json.load(f) |
| credentials[username] = password |
| with open(CREDENTIALS_FILE, "w") as f: |
| json.dump(credentials, f) |
|
|
| |
| def validate_credentials(username, password): |
| with open(CREDENTIALS_FILE, "r") as f: |
| credentials = json.load(f) |
| return credentials.get(username) == password |
|
|
| |
| def username_exists(username): |
| with open(CREDENTIALS_FILE, "r") as f: |
| credentials = json.load(f) |
| return username in credentials |
|
|
| |
| initialize_credentials_file() |
|
|
| |
| if "is_authenticated" not in st.session_state: |
| st.session_state["is_authenticated"] = False |
| if "current_user" not in st.session_state: |
| st.session_state["current_user"] = None |
|
|
| |
| if "income_data" not in st.session_state: |
| st.session_state["income_data"] = pd.DataFrame(columns=["Date", "Category", "Amount"]) |
|
|
| if "expense_data" not in st.session_state: |
| st.session_state["expense_data"] = pd.DataFrame(columns=["Date", "Category", "Amount"]) |
|
|
| if "custom_income_sources" not in st.session_state: |
| st.session_state["custom_income_sources"] = [] |
|
|
| if "custom_expense_categories" not in st.session_state: |
| st.session_state["custom_expense_categories"] = [] |
|
|
| if "saving_goals" not in st.session_state: |
| st.session_state["saving_goals"] = pd.DataFrame(columns=["Goal", "Target Amount", "Current Savings", "Deadline", "Allocated Savings"]) |
|
|
| |
| def format_date(date_obj): |
| return date_obj.strftime("%d-%m-%y") |
|
|
| def update_transaction_log(): |
| st.session_state["transaction_log"] = pd.concat([ |
| st.session_state["income_data"].assign(Type="Income"), |
| st.session_state["expense_data"].assign(Type="Expense") |
| |
| ]).reset_index(drop=True) |
| st.session_state["transaction_log"].index += 1 |
|
|
| if "transaction_log" not in st.session_state: |
| update_transaction_log() |
|
|
| |
| def login_page(): |
| st.title("Login") |
| username = st.text_input("Username") |
| password = st.text_input("Password", type="password") |
| if st.button("Login"): |
| if validate_credentials(username, password): |
| st.session_state["is_authenticated"] = True |
| st.session_state["current_user"] = username |
| st.success("Login successful!") |
| st.rerun() |
| else: |
| st.error("Invalid username or password") |
|
|
| def signup_page(): |
| st.title("Sign Up") |
| username = st.text_input("Choose a Username") |
| password = st.text_input("Choose a Password", type="password") |
| if st.button("Sign Up"): |
| if username_exists(username): |
| st.error("Username already exists. Please choose a different username.") |
| else: |
| save_user_credentials(username, password) |
| st.success("Sign up successful! You can now log in.") |
|
|
| |
| def main_app(): |
| |
| st.sidebar.title("Navigation") |
| tabs = st.sidebar.radio("", ["Home", "Record Income", "Record Expense", "Transaction Log", "Savings", "Budget Recommendations", "Logout"]) |
|
|
| |
| if tabs == "Home": |
| |
| total_income = st.session_state['income_data']['Amount'].sum() |
| total_expense = st.session_state['expense_data']['Amount'].sum() |
| allocated_savings = st.session_state["saving_goals"]["Allocated Savings"].sum() |
| net_savings = total_income - total_expense - allocated_savings |
| |
| |
| cash_in_hand = total_income - total_expense - allocated_savings |
| |
| |
| col1, col2, col3 = st.columns(3) |
| col1.metric("Total Income", f"{total_income:,}") |
| col2.metric("Total Expense", f"{total_expense:,}") |
| col3.metric("Net Savings", f"{net_savings:,}") |
| |
| |
| col4, col5 = st.columns(2) |
| col4.metric("Cash in Hand", f"{cash_in_hand:,}") |
| |
| |
| if not st.session_state["saving_goals"].empty: |
| closest_goal = st.session_state["saving_goals"].iloc[0] |
| closest_goal_amount = closest_goal["Target Amount"] - closest_goal["Current Savings"] |
| closest_goal_name = closest_goal["Goal"] |
| closest_goal_deadline = closest_goal["Deadline"] |
| |
| |
| for _, goal in st.session_state["saving_goals"].iterrows(): |
| remaining_amount = goal["Target Amount"] - goal["Current Savings"] |
| if goal["Deadline"] < closest_goal_deadline and remaining_amount > 0: |
| closest_goal = goal |
| closest_goal_name = goal["Goal"] |
| closest_goal_deadline = goal["Deadline"] |
| closest_goal_amount = remaining_amount |
| |
| |
| st.write(f"Amount needed to reach goal '{closest_goal_name}': {closest_goal_amount:,} (Deadline: {closest_goal_deadline})") |
|
|
| if total_income > 0 and total_expense > 0: |
| |
| labels = ['Expense', 'Savings', 'Unallocated Income'] |
| sizes = [total_expense, allocated_savings, cash_in_hand] |
| sizes = [0 if np.isnan(value) else value for value in sizes] |
| colors = ['#ff9999','#66b3ff','#99ff99'] |
| |
| fig, ax = plt.subplots() |
| ax.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors) |
| ax.axis('equal') |
| st.pyplot(fig) |
| |
| |
| for idx, goal in st.session_state["saving_goals"].iterrows(): |
| goal_name = goal["Goal"] |
| target_amount = goal["Target Amount"] |
| current_savings = goal["Current Savings"] |
| |
| progress = current_savings / target_amount * 100 |
| |
| st.subheader(f"Progress for Goal: {goal_name}") |
| st.progress(progress) |
| st.write(f"Target Amount: {target_amount}, Current Savings: {current_savings}") |
|
|
| |
| elif tabs == "Record Income": |
| st.header("Record Income") |
| with st.form("income_form"): |
| income_date = st.date_input("Date", value=date.today()) |
| income_source = st.selectbox("Source", ["Salary", "Freelance", "Investments"] + st.session_state["custom_income_sources"]) |
| income_amount = st.number_input("Amount", min_value=0, step=1) |
| submit_income = st.form_submit_button("Add Income") |
|
|
| if submit_income: |
| new_entry = {"Date": income_date, "Category": "Income from "+income_source, "Amount": income_amount} |
| st.session_state["income_data"] = pd.concat( |
| [st.session_state["income_data"], pd.DataFrame([new_entry])], ignore_index=True |
| ) |
| update_transaction_log() |
| st.success("Income added successfully!") |
|
|
| if not st.session_state["income_data"].empty: |
| fig, ax = plt.subplots() |
| st.session_state['income_data']['Date'] = pd.to_datetime(st.session_state['income_data']['Date']) |
| sorted_data = st.session_state["income_data"].sort_values("Date") |
| ax.bar(sorted_data["Date"], sorted_data["Amount"], label='Income', width=0.4) |
| ax.plot(sorted_data["Date"], sorted_data["Amount"].cumsum(), label='Cumulative Income', marker='o', color='green') |
| ax.set_title("Income Over Time") |
| ax.set_xlabel("Date") |
| ax.set_ylabel("Amount") |
| plt.xticks(rotation=45) |
| ax.legend() |
| st.pyplot(fig) |
|
|
| |
| elif tabs == "Record Expense": |
| st.header("Record Expense") |
| with st.form("expense_form"): |
| expense_date = st.date_input("Date", value=date.today()) |
| expense_category = st.selectbox("Category", ["Food", "Travel", "Utilities"] + st.session_state["custom_expense_categories"]) |
| expense_amount = st.number_input("Amount", min_value=0, step=1) |
| submit_expense = st.form_submit_button("Add Expense") |
|
|
| if submit_expense: |
| new_entry = {"Date": expense_date, "Category": "Expense for " + expense_category, "Amount": expense_amount} |
| st.session_state["expense_data"] = pd.concat( |
| [st.session_state["expense_data"], pd.DataFrame([new_entry])], ignore_index=True |
| ) |
| update_transaction_log() |
| st.success("Expense added successfully!") |
|
|
| if not st.session_state["expense_data"].empty: |
| fig, ax = plt.subplots() |
| st.session_state['expense_data']['Date'] = pd.to_datetime(st.session_state['expense_data']['Date']) |
| sorted_data = st.session_state["expense_data"].sort_values("Date") |
| ax.bar(sorted_data["Date"], sorted_data["Amount"], label='Expense', width=0.4) |
| ax.plot(sorted_data["Date"], sorted_data["Amount"].cumsum(), label='Cumulative Expense', marker='o', color='red') |
| ax.set_title("Expenses Over Time") |
| ax.set_xlabel("Date") |
| ax.set_ylabel("Amount") |
| plt.xticks(rotation=45) |
| ax.legend() |
| st.pyplot(fig) |
|
|
| |
| elif tabs == "Transaction Log": |
| st.header("Transaction Log") |
| |
| |
| if "income_data" in st.session_state and "expense_data" in st.session_state: |
| |
| |
| transaction_log = pd.concat([ |
| st.session_state["income_data"].assign(Type="Income"), |
| st.session_state["expense_data"].assign(Type="Expense") |
| ]).sort_values(by="Date") |
|
|
| if "saving_transaction_log" in st.session_state: |
| transaction_log = pd.concat([ |
| st.session_state["income_data"].assign(Type="Income"), |
| st.session_state["expense_data"].assign(Type="Expense"), |
| st.session_state["saving_transaction_log"].assign(Type="Saving") |
| ]).sort_values(by="Date") |
| |
| |
| transaction_log["Date"] = pd.to_datetime(transaction_log["Date"]) |
| |
| |
| st.dataframe(transaction_log) |
| else: |
| st.warning("No transactions recorded yet.") |
|
|
|
|
| |
| elif tabs == "Savings": |
| st.header("Savings Goals") |
| |
| |
| total_income = st.session_state['income_data']['Amount'].sum() |
| total_expense = st.session_state['expense_data']['Amount'].sum() |
| net_savings = 0 |
| max_savings = total_income - total_expense - net_savings |
| |
| |
| if "show_set_goal_form" not in st.session_state: |
| st.session_state["show_set_goal_form"] = False |
| if "show_allocate_savings_form" not in st.session_state: |
| st.session_state["show_allocate_savings_form"] = False |
| |
| |
| set_goal_button = st.button("Set Saving Goal") |
| allocate_savings_button = st.button("Allocate Saving to Goal") |
| |
| |
| if set_goal_button: |
| st.session_state["show_set_goal_form"] = True |
| st.session_state["show_allocate_savings_form"] = False |
| if allocate_savings_button: |
| st.session_state["show_allocate_savings_form"] = True |
| st.session_state["show_set_goal_form"] = False |
| |
| |
| if st.session_state["show_set_goal_form"]: |
| st.subheader("Set Saving Goal") |
| goal_name = st.text_input("Goal Name") |
| target_amount = st.number_input("Target Amount", min_value=0, step=100) |
| goal_deadline = st.date_input("Deadline", value=date.today()) |
| |
| |
| if st.button("Create Goal"): |
| if goal_name and target_amount > 0: |
| new_goal = {"Goal": goal_name, |
| "Target Amount": target_amount, |
| "Current Savings": net_savings, |
| "Deadline": goal_deadline, |
| "Allocated Savings": 0} |
| st.session_state["saving_goals"] = pd.concat( |
| [st.session_state["saving_goals"], pd.DataFrame([new_goal])], ignore_index=True |
| ) |
| st.success("Goal set successfully!") |
| else: |
| st.error("Please provide valid goal name and target amount.") |
| |
| |
| if st.session_state["show_allocate_savings_form"]: |
| st.subheader("Allocate Savings to Goals") |
| if not st.session_state["saving_goals"].empty: |
| |
| goal_name_selected = st.selectbox("Select Saving Goal", st.session_state["saving_goals"]["Goal"].values) |
| |
| if goal_name_selected: |
| |
| selected_goal = st.session_state["saving_goals"][st.session_state["saving_goals"]["Goal"] == goal_name_selected].iloc[0] |
| |
| |
| st.write(f"**{goal_name_selected}**") |
| st.write(f"Target Amount: {selected_goal['Target Amount']}, Current Savings: {selected_goal['Current Savings']}, Deadline: {selected_goal['Deadline']}") |
| |
| |
| allocated_savings = st.number_input(f"Enter Amount to Allocate for {goal_name_selected}", |
| min_value=0, max_value=max_savings, step=100, key=f"allocate_{goal_name_selected}") |
| |
| |
| save_allocation_button = st.button(f"Save Allocation for {goal_name_selected}") |
| |
| if save_allocation_button and allocated_savings > 0: |
| |
| idx = st.session_state["saving_goals"][st.session_state["saving_goals"]["Goal"] == goal_name_selected].index[0] |
| |
| |
| st.session_state["saving_goals"].at[idx, "Allocated Savings"] += allocated_savings |
| max_savings -= allocated_savings |
| st.session_state["saving_goals"].at[idx, "Current Savings"] += allocated_savings |
| |
| |
| if "saving_transaction_log" not in st.session_state: |
| st.session_state["saving_transaction_log"] = pd.DataFrame(columns=["Date", "Amount", "Category"]) |
| |
| |
| transaction_entry = { |
| "Date": pd.to_datetime("today"), |
| "Amount": allocated_savings, |
| "Category": "Savings for " + goal_name_selected |
| } |
| |
| |
| st.session_state["saving_transaction_log"] = st.session_state["saving_transaction_log"].append(transaction_entry, ignore_index=True) |
| |
| |
| st.success(f"Allocated {allocated_savings} to goal '{goal_name_selected}'") |
| |
| elif save_allocation_button: |
| st.error("Please allocate a valid amount greater than 0.") |
|
|
|
|
|
|
|
|
|
|
|
|
| else: |
| st.write("No saving goals set yet.") |
|
|
| |
| |
| if not st.session_state["saving_goals"].empty: |
| st.subheader("Current Goals and Allocated Savings") |
| for idx, goal in st.session_state["saving_goals"].iterrows(): |
| progress = (goal["Current Savings"] / goal["Target Amount"]) if goal["Target Amount"] > 0 else 0 |
| st.write(f"**{goal['Goal']}**") |
| st.write(f"Target Amount: {goal['Target Amount']}, Current Savings: {goal['Current Savings']}, Deadline: {goal['Deadline']}") |
| st.progress(progress) |
| st.write(f"Progress: {progress * 100:.2f}%") |
| |
| |
| delete_goal_idx = st.selectbox("Select Goal to Delete", options=[""] + list(st.session_state["saving_goals"]["Goal"])) |
| if delete_goal_idx: |
| st.session_state["saving_goals"] = st.session_state["saving_goals"][st.session_state["saving_goals"]["Goal"] != delete_goal_idx] |
| st.success(f"Goal '{delete_goal_idx}' deleted successfully.") |
| else: |
| st.write("No savings goals set yet.") |
|
|
|
|
| |
| elif tabs == "Budget Recommendations": |
| st.header("Budget Recommendations") |
| if st.session_state["income_data"].empty or st.session_state["expense_data"].empty: |
| st.warning("Please record your income and expenses before getting budget recommendations.") |
| else: |
| total_income = st.session_state["income_data"]["Amount"].sum() |
| total_expense = st.session_state["expense_data"]["Amount"].sum() |
|
|
| |
| budget_allocation = { |
| "Housing": total_income * 0.30, |
| "Transportation": total_income * 0.15, |
| "Food": total_income * 0.10, |
| "Savings": total_income * 0.20, |
| "Entertainment": total_income * 0.05, |
| "Health": total_income * 0.10, |
| "Miscellaneous": total_income * 0.10 |
| } |
| st.write("Budget Recommendations based on your income:") |
| for category, amount in budget_allocation.items(): |
| st.write(f"{category}: {amount:,.2f}") |
|
|
| |
| elif tabs == "Logout": |
| st.session_state["is_authenticated"] = False |
| st.session_state["current_user"] = None |
| st.success("Logged out successfully!") |
| st.rerun() |
|
|
| |
| if not st.session_state["is_authenticated"]: |
| page = st.selectbox("Choose a page", ["Login", "Sign Up"]) |
| if page == "Login": |
| login_page() |
| elif page == "Sign Up": |
| signup_page() |
| else: |
| main_app() |
|
|