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("""

💰 Revenue

{:.1f}M NOK

+{:.0f}% 🔥

""".format(metrics.get('revenue_2024_m', 0), metrics.get('revenue_growth', 0)), unsafe_allow_html=True) with col2: st.markdown("""

📈 Net Profit

{:.1f}M NOK

Profitable ✅

""".format(metrics.get('net_profit_2024_m', 0)), unsafe_allow_html=True) with col3: st.markdown("""

📊 Gross Margin

{:.1f}%

Healthy 💪

""".format(metrics.get('gross_margin_2024', 0)), unsafe_allow_html=True) with col4: st.markdown("""

🎯 Net Margin

{:.1f}%

Strong 📈

""".format(metrics.get('net_margin_2024', 0)), unsafe_allow_html=True) # Charts section col1, col2 = st.columns(2) with col1: st.plotly_chart(analyzer.create_revenue_trend_chart(), use_container_width=True) with col2: st.plotly_chart(analyzer.create_financial_health_radar(), use_container_width=True) # Quick insights st.markdown("""

🎯 Quick Insights

""".format( metrics.get('revenue_growth', 0), metrics.get('net_margin_2024', 0), metrics.get('gross_margin_2024', 0) ), unsafe_allow_html=True) def income_analysis_page(analyzer): """Income statement analysis page""" st.header("💰 Income Analysis") if analyzer.data is None: st.warning("Please upload data to begin analysis.") return # Revenue analysis st.subheader("📈 Revenue Trend Analysis") st.plotly_chart(analyzer.create_revenue_trend_chart(), use_container_width=True) # Cost structure st.subheader("🥧 Cost Structure Analysis") if analyzer.processed_data: data = analyzer.processed_data costs = ['Cost of Goods Sold', 'Operating Expenses', 'Financial Expenses'] values = [data['cogs_2024'], 2000000, 234567] # Sample values fig = px.pie(values=values, names=costs, title="Cost Breakdown 2024") st.plotly_chart(fig, use_container_width=True) # Profitability metrics st.subheader("📊 Profitability Indicators") metrics = analyzer.calculate_metrics() col1, col2, col3 = st.columns(3) with col1: st.metric("Gross Margin", f"{metrics.get('gross_margin_2024', 0):.1f}%") with col2: st.metric("Net Margin", f"{metrics.get('net_margin_2024', 0):.1f}%") with col3: st.metric("Revenue Growth", f"{metrics.get('revenue_growth', 0):.1f}%") def balance_sheet_page(analyzer): """Balance sheet analysis page""" st.header("🏛️ Balance Sheet Analysis") if analyzer.data is None: st.warning("Please upload balance sheet data to begin analysis.") return st.info("Balance sheet analysis requires additional data. Please upload complete financial statements.") # Sample asset structure chart assets = ['Current Assets', 'Fixed Assets', 'Intangible Assets'] values = [45, 35, 20] fig = px.pie(values=values, names=assets, title="Asset Structure") st.plotly_chart(fig, use_container_width=True) def cash_flow_page(analyzer): """Cash flow analysis page""" st.header("💸 Cash Flow Analysis") if analyzer.data is None: st.warning("Please upload cash flow data to begin analysis.") return st.info("Cash flow analysis requires additional data. Please upload complete cash flow statements.") # Sample cash flow chart categories = ['Operating CF', 'Investing CF', 'Financing CF'] values = [5000000, -2000000, -1000000] fig = go.Figure(go.Waterfall( name="Cash Flow", orientation="v", measure=["relative", "relative", "relative"], x=categories, y=values, text=[f"{v/1000000:.1f}M" for v in values] )) fig.update_layout(title="Cash Flow Waterfall") st.plotly_chart(fig, use_container_width=True) def ratios_page(analyzer): """Financial ratios analysis page""" st.header("📊 Financial Ratios Hub") if analyzer.data is None: st.warning("Please upload data to calculate ratios.") return # Ratio categories col1, col2, col3, col4 = st.columns(4) with col1: if st.button("Profitability"): st.session_state.ratio_category = "profitability" with col2: if st.button("Liquidity"): st.session_state.ratio_category = "liquidity" with col3: if st.button("Efficiency"): st.session_state.ratio_category = "efficiency" with col4: if st.button("Growth"): st.session_state.ratio_category = "growth" # Display ratios based on selection metrics = analyzer.calculate_metrics() st.subheader("Key Financial Ratios") col1, col2, col3 = st.columns(3) with col1: st.metric("Gross Profit Margin", f"{metrics.get('gross_margin_2024', 0):.1f}%", "A+") with col2: st.metric("Net Profit Margin", f"{metrics.get('net_margin_2024', 0):.1f}%", "A") with col3: st.metric("Revenue Growth", f"{metrics.get('revenue_growth', 0):.1f}%", "A+") def ai_assistant_page(analyzer): """AI finance assistant page""" st.header("🤖 AI Finance Assistant") if analyzer.data is None: st.warning("Please upload data to enable AI analysis.") return # Chat interface st.subheader("💬 Ask Your Financial Questions") # Predefined questions col1, col2 = st.columns(2) with col1: if st.button("Analyze my financial health"): st.session_state.ai_query = "financial_health" if st.button("Find the biggest risks"): st.session_state.ai_query = "risks" with col2: if st.button("Give investment advice"): st.session_state.ai_query = "investment" if st.button("Create improvement plan"): st.session_state.ai_query = "improvement" # Text input for custom questions user_question = st.text_input("Or ask your own question:") if user_question or 'ai_query' in st.session_state: metrics = analyzer.calculate_metrics() # Simple AI-like responses based on data if user_question or st.session_state.get('ai_query') == 'financial_health': st.markdown("""

🎯 Financial Health Analysis

Based on your financial data:

""".format( metrics.get('revenue_growth', 0), metrics.get('net_margin_2024', 0) ), unsafe_allow_html=True) # Clear the session state if 'ai_query' in st.session_state: del st.session_state.ai_query if __name__ == "__main__": main()