Hi !
Hello,
I don’t understand why my app reloads when I select the selectbox. I’ve set up cache management, managed states but the result is still the same.
Do you see where the problem could be coming from?
My code :
import os
import pandas as pd
import streamlit as st
from datetime import datetime
import numpy as np
import plotly.express as px
import plotly.graph_objs as go
import chardet
st.set_page_config(page_title="Bank statement analyzer", page_icon="🐈", layout="wide")
@st.cache_data(show_spinner=False)
def get_encoding(file): #Model
"""Detect the encoding of a file."""
result = chardet.detect(file.read())
return result['encoding']
@st.cache_data(show_spinner=False)
def read_csv(file, delimiter, encoding): #Model
"""Read a CSV file with the specified delimiter and encoding."""
file.seek(0)
return pd.read_csv(file, delimiter=delimiter, encoding=encoding)
class FinancialApp:
def __init__(self):
self.data = None
self.delimiter = ';'
self.currency = '€'
def detect_headers(self): #Model
"""Detect the headers of the CSV file."""
return list(self.data.columns)
def process_dates(self, start_date, end_date): #Model
"""Process the dates and return filtered data to display comparative graphs."""
def process_data(self): #Model
"""Process the data in the CSV file."""
self.show_tabs()
def clean_number(self, num): #Model
"""Clean a number string by removing non-numeric characters and converting it to a float."""
try:
return float(str(num).replace(",", "."))
except ValueError:
return np.nan
def match_columns(self, headers): #Controller
"""Match the columns in the CSV file to the required columns."""
def display_monthly_expenses(self):
st.header("Monthly Expense Analysis")
# Get unique years and months from the data
self.data['Year'] = self.data[self.date_column].dt.year
self.data['Month'] = self.data[self.date_column].dt.month
unique_years = sorted(self.data['Year'].unique())
unique_months = sorted(self.data['Month'].unique())
# Initialize session state for year, month, and category if not already set
if 'selected_year' not in st.session_state:
st.session_state['selected_year'] = unique_years[0]
if 'selected_month' not in st.session_state:
st.session_state['selected_month'] = unique_months[0]
if 'selected_category' not in st.session_state:
st.session_state['selected_category'] = None
# Year and Month SelectBox
selected_year = st.selectbox("Select Year", unique_years, index=unique_years.index(st.session_state['selected_year']), key='year_selectbox')
selected_month = st.selectbox("Select Month", unique_months, index=unique_months.index(st.session_state['selected_month']), key='month_selectbox')
# Update session state if the selections change
st.session_state['selected_year'] = selected_year
st.session_state['selected_month'] = selected_month
filtered_data = self.data[(self.data['Year'] == st.session_state['selected_year']) & (self.data['Month'] == st.session_state['selected_month'])]
if not filtered_data.empty:
# Group by category
expenses_by_category = filtered_data.groupby(self.category_column)[self.debit_column].sum().reset_index()
st.subheader(f"Expenses for {st.session_state['selected_month']}/{st.session_state['selected_year']}")
st.dataframe(expenses_by_category)
# Pie chart of expenses per category
fig = px.pie(expenses_by_category, values=self.debit_column, names=self.category_column, title="Expenses by Category")
st.plotly_chart(fig, use_container_width=True)
# Add click interaction for category selection
categories = expenses_by_category[self.category_column].tolist()
if st.session_state['selected_category'] not in categories:
st.session_state['selected_category'] = categories[0]
selected_category = st.selectbox("Select Category to view details", categories, index=categories.index(st.session_state['selected_category']), key='category_selectbox')
# Update session state for selected category
st.session_state['selected_category'] = selected_category
if st.session_state['selected_category'] == True:
category_expenses = filtered_data[filtered_data[self.category_column] == st.session_state['selected_category']]
st.subheader(f"Details for category: {st.session_state['selected_category']}")
st.dataframe(category_expenses)
else:
st.write(f"No data available for {st.session_state['selected_month']}/{st.session_state['selected_year']}")
def show_summary(self): #View
"""Show a summary of the financial data."""
st.header("Summary")
st.subheader("CSV File")
st.dataframe(self.data)
self.show_charts()
def show_charts(self): #View
"""Show charts of the financial data."""
def run(self): #Controller
with st.sidebar:
st.title('FinancialAnalysisApp')
with st.expander("Settings", expanded=True):
self.delimiter = st.text_input("Delimiter (default ';')", value=";", key="delimiter")
self.currency = st.text_input("Currency (default '€')", value="€", key="currency")
self.savings = st.number_input(f"How much money do you want to save every month? (default {self.currency}1000)", value=1000, key="savings")
self.date_format = st.text_input("Date format (default '%d-%m-%Y')", value="%d-%m-%Y", key="date_format")
self.file = st.file_uploader("Upload your CSV file", type=['csv'], key="file_uploader")
if st.session_state.get('file_uploader') is not None:
try:
encoding = get_encoding(st.session_state['file_uploader'])
self.data = read_csv(st.session_state['file_uploader'], st.session_state['delimiter'], encoding)
headers = self.detect_headers()
with st.sidebar:
with st.expander("Match columns", expanded=True):
self.match_columns(headers)
# Initialize session state for the process button if not already set
if 'process' not in st.session_state:
st.session_state['process'] = False
# Define the process button
if st.button("Process", key="process_button"):
st.session_state['process'] = True
# Execute processing if the process button was clicked
if st.session_state['process'] == True:
with st.spinner("Processing data..."):
self.process_data()
st.session_state['process'] = False
except Exception as e:
with st.sidebar:
st.write("Error: ", e)
def show_tabs(self):
tab1, tab2 = st.tabs(["Summary", "Monthly Analysis"])
with tab1:
self.show_summary()
with tab2:
self.display_monthly_expenses()
if __name__ == "__main__":
app = FinancialApp()
app.run()