import streamlit as st import pandas as pd import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots import numpy as np from datetime import datetime import io import os import warnings warnings.filterwarnings('ignore') # Initialize session state if 'data_loaded' not in st.session_state: st.session_state.data_loaded = False if 'analyzer' not in st.session_state: st.session_state.analyzer = None # Page configuration st.set_page_config( page_title="📊 FinanceGPT Analyzer", page_icon="📊", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS for better styling st.markdown(""" """, unsafe_allow_html=True) # 修复后的文件加载函数 @st.cache_data def load_csv_with_encoding(file_content: bytes, filename: str) -> pd.DataFrame: """Load CSV with automatic encoding detection - cached""" try: import chardet detected = chardet.detect(file_content) encoding = detected['encoding'] if detected['encoding'] else 'utf-8' try: from io import BytesIO return pd.read_csv(BytesIO(file_content), encoding=encoding) except: encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1'] for enc in encodings: try: return pd.read_csv(BytesIO(file_content), encoding=enc) except: continue raise Exception("Cannot read file with any encoding") except ImportError: # Fallback if chardet is not available from io import BytesIO encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1'] for enc in encodings: try: return pd.read_csv(BytesIO(file_content), encoding=enc) except: continue raise Exception("Cannot read file with any encoding") @st.cache_data def load_excel_file(file_content: bytes) -> pd.DataFrame: """Load Excel file - cached""" from io import BytesIO return pd.read_excel(BytesIO(file_content)) def load_data(uploaded_file): """Unified data loading function""" file_content = uploaded_file.read() uploaded_file.seek(0) if uploaded_file.name.endswith('.csv'): return load_csv_with_encoding(file_content, uploaded_file.name) else: return load_excel_file(file_content) class FinanceAnalyzer: def __init__(self): self.data = None self.processed_data = {} def load_csv_data(self): """Load CSV data from local file""" try: # Try multiple possible paths for CSV file possible_paths = [ os.path.join(os.path.dirname(__file__), '2024.csv'), os.path.join('2024.csv'), '2024.csv', os.path.join('2024.csv') ] for csv_path in possible_paths: if os.path.exists(csv_path): # Use the improved loading function with open(csv_path, 'rb') as f: file_content = f.read() df = load_csv_with_encoding(file_content, csv_path) st.success(f"✅ CSV data loaded from: {csv_path}") return df # If no file found, show available files for debugging current_dir = os.path.dirname(__file__) if __file__ else '.' available_files = [] for root, dirs, files in os.walk(current_dir): for file in files: if file.endswith('.csv'): available_files.append(os.path.join(root, file)) if available_files: st.warning(f"CSV file not found in expected locations. Available CSV files: {available_files}") else: st.warning("No CSV files found. Using sample data instead.") return self.load_sample_data() except Exception as e: st.error(f"Error loading CSV file: {e}") st.info("Falling back to sample data...") return self.load_sample_data() def load_sample_data(self): """Load sample financial data based on your real CSV structure""" sample_data = { 'Statement_Type': ['Income Statement'] * 19 + ['Balance Sheet'] * 10 + ['Cash Flow Statement'] * 5, 'Account_Name_Norwegian': [ # Income Statement 'Salgsinntekt', 'Sum inntekter', 'Varekostnad', 'Lønnskostnad', 'Avskrivning på varige driftsmidler og immaterielle eiendeler', 'Annen driftskostnad', 'Sum kostnader', 'Driftsresultat', 'Annen renteinntekt', 'Annen finansinntekt', 'Sum finansinntekter', 'Annen rentekostnad', 'Annen finanskostnad', 'Sum finanskostnader', 'Netto finans', 'Resultat før skattekostnad', 'Skattekostnad', 'Årsresultat', 'Overføringer til/fra annen egenkapital', # Balance Sheet 'Sum anleggsmidler', 'Kundefordringer', 'Andre fordringer', 'Sum fordringer', 'Bankinnskudd kontanter og lignende', 'Sum omløpsmidler', 'SUM EIENDELER', 'Sum egenkapital', 'Sum langsiktig gjeld', 'Sum kortsiktig gjeld', 'Sum gjeld', # Cash Flow 'Årsresultat', 'Avskrivninger', 'Netto kontantstrøm fra driftsaktiviteter', 'Netto kontantstrøm fra investeringsaktiviteter', 'NETTO ENDRING I KONTANTER' ], 'Account_Name_English': [ # Income Statement 'Sales Revenue', 'Total Income', 'Cost of Goods Sold', 'Payroll Expenses', 'Depreciation on Fixed Assets and Intangible Assets', 'Other Operating Expenses', 'Total Expenses', 'Operating Result', 'Other Interest Income', 'Other Financial Income', 'Total Financial Income', 'Other Interest Expenses', 'Other Financial Expenses', 'Total Financial Expenses', 'Net Financial Result', 'Result Before Tax', 'Tax Expense', 'Annual Result', 'Transfers to/from Other Equity', # Balance Sheet 'Total Fixed Assets', 'Customer Receivables', 'Other Receivables', 'Total Receivables', 'Bank Deposits Cash and Similar', 'Total Current Assets', 'TOTAL ASSETS', 'Total Equity', 'Total Long-term Debt', 'Total Short-term Debt', 'Total Debt', # Cash Flow 'Net Income', 'Depreciation', 'Net Cash Flow from Operating Activities', 'Net Cash Flow from Investing Activities', 'NET CHANGE IN CASH' ], '2024_Amount_NOK': [ # Income Statement 25107008, 25107008, 9880032, 3700289, 316180, 4355621, 18252121, 6854887, 11439, 1230, 12669, 51288, 3916, 55205, -42536, 6812351, 1498717, 5313634, 5313634, # Balance Sheet 4282396, 5575707, 178797, 5754504, 1595549, 7350053, 11632449, 5602404, 653459, 5376586, 6030045, # Cash Flow 5313634, 316180, 3385812, -3546128, 801948 ], '2023_Amount_NOK': [ # Income Statement 4891891, 4891891, 770840, 2703253, 0, 1330101, 4804194, 87697, 385, 0, 385, 59498, 0, 59498, -59113, 28584, 32524, -3940, -3940, # Balance Sheet 1052447, 2000151, 233394, 2233546, 793599, 3027145, 4079592, 288770, 630673, 3160150, 3790823, # Cash Flow -3940, 0, 951553, -1052448, 500891 ] } return pd.DataFrame(sample_data) def process_financial_data(self, df): """Process uploaded financial data - improved version""" self.data = df try: # Handle the actual CSV structure (your format) if 'Statement_Type' in df.columns and '2024_Amount_NOK' in df.columns: # Filter income statement data income_df = df[df['Statement_Type'] == 'Income Statement'].copy() # Extract key financial metrics revenue_rows = income_df[income_df['Account_Name_English'].str.contains('Sales Revenue', case=False, na=False)] profit_rows = income_df[income_df['Account_Name_English'].str.contains('Annual Result|Net Income', case=False, na=False)] cogs_rows = income_df[income_df['Account_Name_English'].str.contains('Cost of Goods', case=False, na=False)] operating_rows = income_df[income_df['Account_Name_English'].str.contains('Operating Result', case=False, na=False)] self.processed_data = { 'revenue_2024': revenue_rows['2024_Amount_NOK'].iloc[0] if len(revenue_rows) > 0 else 0, 'revenue_2023': revenue_rows['2023_Amount_NOK'].iloc[0] if len(revenue_rows) > 0 else 0, 'net_profit_2024': profit_rows['2024_Amount_NOK'].iloc[0] if len(profit_rows) > 0 else 0, 'net_profit_2023': profit_rows['2023_Amount_NOK'].iloc[0] if len(profit_rows) > 0 else 0, 'cogs_2024': abs(cogs_rows['2024_Amount_NOK'].iloc[0]) if len(cogs_rows) > 0 else 0, 'cogs_2023': abs(cogs_rows['2023_Amount_NOK'].iloc[0]) if len(cogs_rows) > 0 else 0, 'operating_profit_2024': operating_rows['2024_Amount_NOK'].iloc[0] if len(operating_rows) > 0 else 0, 'operating_profit_2023': operating_rows['2023_Amount_NOK'].iloc[0] if len(operating_rows) > 0 else 0, } st.success("✅ Financial data processed successfully!") st.info(f"📊 Processed {len(df)} financial line items") else: # Fallback for different structures st.warning("⚠️ Using fallback data processing") self.processed_data = { 'revenue_2024': 25107008, 'revenue_2023': 4891891, 'net_profit_2024': 5313634, 'net_profit_2023': -3940, 'cogs_2024': 9880032, 'cogs_2023': 770840, 'operating_profit_2024': 6854887, 'operating_profit_2023': 87697, } except Exception as e: st.error(f"Error processing data: {e}") st.info("Using default financial values...") self.processed_data = { 'revenue_2024': 25107008, 'revenue_2023': 4891891, 'net_profit_2024': 5313634, 'net_profit_2023': -3940, 'cogs_2024': 9880032, 'cogs_2023': 770840, 'operating_profit_2024': 6854887, 'operating_profit_2023': 87697, } def calculate_metrics(self): """Calculate key financial metrics""" if not self.processed_data: return {} data = self.processed_data # Growth rates revenue_growth = ((data['revenue_2024'] - data['revenue_2023']) / abs(data['revenue_2023']) * 100) if data['revenue_2023'] != 0 else 0 # Profitability ratios gross_margin_2024 = ((data['revenue_2024'] - data['cogs_2024']) / data['revenue_2024'] * 100) if data['revenue_2024'] != 0 else 0 net_margin_2024 = (data['net_profit_2024'] / data['revenue_2024'] * 100) if data['revenue_2024'] != 0 else 0 return { 'revenue_growth': revenue_growth, 'gross_margin_2024': gross_margin_2024, 'net_margin_2024': net_margin_2024, 'revenue_2024_m': data['revenue_2024'] / 1000000, 'net_profit_2024_m': data['net_profit_2024'] / 1000000, } def create_revenue_trend_chart(self): """Create revenue trend visualization""" if not self.processed_data: return go.Figure() fig = go.Figure() years = [2023, 2024] revenues = [self.processed_data['revenue_2023']/1000000, self.processed_data['revenue_2024']/1000000] net_profits = [self.processed_data['net_profit_2023']/1000000, self.processed_data['net_profit_2024']/1000000] fig.add_trace(go.Scatter(x=years, y=revenues, mode='lines+markers', name='Revenue (M NOK)', line=dict(color='#1f77b4', width=3))) fig.add_trace(go.Scatter(x=years, y=net_profits, mode='lines+markers', name='Net Profit (M NOK)', line=dict(color='#ff7f0e', width=3))) fig.update_layout(title='Revenue vs Profit Trend', xaxis_title='Year', yaxis_title='Amount (M NOK)', height=400) return fig def create_financial_health_radar(self): """Create financial health radar chart""" metrics = self.calculate_metrics() categories = ['Revenue Growth', 'Gross Margin', 'Net Margin', 'Profitability', 'Efficiency'] values = [ min(metrics.get('revenue_growth', 0) / 5, 100), # Scale revenue growth metrics.get('gross_margin_2024', 0), max(metrics.get('net_margin_2024', 0), 0), 70, # Sample value 65 # Sample value ] fig = go.Figure() fig.add_trace(go.Scatterpolar( r=values, theta=categories, fill='toself', name='Financial Health' )) fig.update_layout( polar=dict( radialaxis=dict(visible=True, range=[0, 100]) ), title="Financial Health Score", height=400 ) return fig def main(): st.title("📊 FinanceGPT Analyzer") st.markdown("### Professional Financial Analysis Dashboard") # Debug information (can be removed in production) with st.expander("🔧 Debug Information"): st.write("**Current working directory:**", os.getcwd()) st.write("**Available files:**") for root, dirs, files in os.walk('.'): for file in files[:10]: # Limit to first 10 files st.write(f"- {os.path.join(root, file)}") # Initialize analyzer if st.session_state.analyzer is None: st.session_state.analyzer = FinanceAnalyzer() analyzer = st.session_state.analyzer # Sidebar navigation with st.sidebar: st.header("Navigation") page = st.selectbox("Choose Analysis Page", [ "🏠 Dashboard", "💰 Income Analysis", "🏛️ Balance Sheet Analysis", "💸 Cash Flow Analysis", "📊 Financial Ratios Hub", "🤖 AI Finance Assistant" ]) st.header("Data Upload") uploaded_file = st.file_uploader("Upload CSV file", type=['csv']) if st.button("Load CSV Data"): try: df = analyzer.load_csv_data() analyzer.process_financial_data(df) st.session_state.data_loaded = True st.success("CSV data loaded successfully!") st.rerun() except Exception as e: st.error(f"Error loading CSV data: {e}") if st.button("Use Sample Data"): analyzer.data = analyzer.load_sample_data() analyzer.process_financial_data(analyzer.data) st.session_state.data_loaded = True st.success("Sample data loaded!") st.rerun() # 修复后的文件上传处理 if uploaded_file is not None: try: # Use the improved file loading function df = load_data(uploaded_file) analyzer.data = df analyzer.process_financial_data(df) st.session_state.data_loaded = True st.success("✅ Data uploaded and processed successfully!") # Show data preview st.subheader("📋 Data Preview") st.write("**Shape:**", df.shape) st.write("**Columns:**", list(df.columns)) st.dataframe(df.head()) st.rerun() except Exception as e: st.error(f"❌ Error loading file: {e}") st.info("💡 Please ensure your CSV file has the correct format with columns: Statement_Type, Account_Name_Norwegian, Account_Name_English, 2024_Amount_NOK, 2023_Amount_NOK") # Main content based on selected page if page == "🏠 Dashboard": dashboard_page(analyzer) elif page == "💰 Income Analysis": income_analysis_page(analyzer) elif page == "🏛️ Balance Sheet Analysis": balance_sheet_page(analyzer) elif page == "💸 Cash Flow Analysis": cash_flow_page(analyzer) elif page == "📊 Financial Ratios Hub": ratios_page(analyzer) elif page == "🤖 AI Finance Assistant": ai_assistant_page(analyzer) def dashboard_page(analyzer): """Main dashboard page""" st.header("📊 Financial Dashboard") if analyzer.data is None: st.warning("Please upload data or use sample data to begin analysis.") return metrics = analyzer.calculate_metrics() # Key metrics cards col1, col2, col3, col4 = st.columns(4) with col1: st.markdown("""
+{:.0f}% 🔥
Profitable ✅
Healthy 💪
Strong 📈
Based on your financial data: