walaa2022 commited on
Commit
c7a28ea
·
verified ·
1 Parent(s): 195e9d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +380 -217
app.py CHANGED
@@ -3,8 +3,10 @@ import gradio as gr
3
  import pandas as pd
4
  import torch
5
  import logging
6
- from transformers import pipeline, AutoModelForSequenceClassification, AutoTokenizer
7
  import gc
 
 
 
8
 
9
  # Setup logging
10
  logging.basicConfig(
@@ -17,38 +19,100 @@ logger = logging.getLogger(__name__)
17
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
18
  logger.info(f"Using device: {DEVICE}")
19
 
20
- def clear_gpu_memory():
21
- """Utility function to clear GPU memory"""
22
- if DEVICE == "cuda":
23
- torch.cuda.empty_cache()
24
- gc.collect()
25
-
26
- def validate_financial_csv(file_obj, file_type):
27
- """Validate financial CSV files"""
28
- try:
29
- df = pd.read_csv(file_obj)
30
-
31
- # Expected columns based on file type
32
- expected_columns = {
33
- 'income_statement': ['Revenue', 'Expenses', 'Profit'],
34
- 'balance_sheet': ['Assets', 'Liabilities', 'Equity']
35
- }
36
 
37
- # Check for minimum required columns
38
- found_columns = set(df.columns)
39
- required_columns = set(expected_columns.get(file_type, []))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- if not any(col in found_columns for col in required_columns):
42
- return False, f"Missing required columns. Expected at least one of: {required_columns}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- return True, "Valid CSV file"
45
- except Exception as e:
46
- return False, f"Invalid CSV file: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  class FinancialAnalyzer:
49
- """Simplified Financial Analyzer using small models"""
50
 
51
  def __init__(self):
 
52
  self.sentiment_model = None
53
  self.analysis_model = None
54
  self.load_models()
@@ -64,7 +128,7 @@ class FinancialAnalyzer:
64
  truncation=True
65
  )
66
 
67
- # Load small model for analysis and recommendations
68
  self.analysis_model = pipeline(
69
  "text-generation",
70
  model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
@@ -76,173 +140,166 @@ class FinancialAnalyzer:
76
  logger.error(f"Error loading models: {str(e)}")
77
  raise
78
 
79
- def process_csv(self, file_obj):
80
- """Process CSV file and extract KPIs"""
81
  try:
82
- if file_obj is None:
83
- raise ValueError("No file provided")
84
-
85
- # Read CSV with better column handling
86
- df = pd.read_csv(file_obj, skipinitialspace=True)
87
 
88
- if df.empty:
89
- raise ValueError("Empty CSV file")
90
-
91
- # Clean column names
92
- df.columns = df.columns.str.strip()
93
-
94
- # Log the columns found
95
- logger.info(f"Found columns: {df.columns.tolist()}")
96
-
97
- # Remove any unnamed columns
98
- df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
99
-
100
- # Convert columns to numeric where possible
101
- for col in df.columns:
102
- df[col] = pd.to_numeric(df[col].str.replace('[$,()]', '', regex=True), errors='ignore')
103
-
104
- # Get numeric columns
105
- numeric_cols = df.select_dtypes(include=['float64', 'int64']).columns
106
- if len(numeric_cols) == 0:
107
- raise ValueError("No numeric columns found in CSV")
108
-
109
- logger.info(f"Numeric columns: {numeric_cols.tolist()}")
110
-
111
- # Calculate meaningful KPIs
112
- kpis = {
113
- 'total_revenue': df[numeric_cols].sum().sum(),
114
- 'average_values': df[numeric_cols].mean(),
115
- 'year_over_year_growth': df[numeric_cols].pct_change().mean() * 100,
116
- 'key_metrics': df[numeric_cols].describe()
117
  }
118
 
119
- return df[numeric_cols].describe(), kpis
120
 
121
  except Exception as e:
122
- logger.error(f"Error processing CSV: {str(e)}")
123
- raise
124
-
125
- def analyze_financials(self, income_summary, balance_summary):
126
- """Generate financial analysis and recommendations"""
127
- try:
128
- # Extract meaningful metrics
129
- income_metrics = {
130
- 'Total Revenue': income_summary[1]['total_revenue'],
131
- 'Average Values': income_summary[1]['average_values'].mean(),
132
- 'Growth Rate': income_summary[1]['year_over_year_growth'].mean()
133
- }
134
-
135
- balance_metrics = {
136
- 'Total Assets': balance_summary[1]['total_revenue'],
137
- 'Average Values': balance_summary[1]['average_values'].mean(),
138
- 'Growth Rate': balance_summary[1]['year_over_year_growth'].mean()
139
- }
140
 
141
- financial_context = f"""
142
- Income Statement Analysis:
143
- - Total Revenue: ${income_metrics['Total Revenue']:,.2f}
144
- - Average Revenue: ${income_metrics['Average Values']:,.2f}
145
- - Growth Rate: {income_metrics['Growth Rate']:.2f}%
146
-
147
- Detailed Income Metrics:
148
- {income_summary[0].to_string()}
149
-
150
- Balance Sheet Analysis:
151
- - Total Assets: ${balance_metrics['Total Assets']:,.2f}
152
- - Average Assets: ${balance_metrics['Average Values']:,.2f}
153
- - Growth Rate: {balance_metrics['Growth Rate']:.2f}%
154
-
155
- Detailed Balance Metrics:
156
- {balance_summary[0].to_string()}
157
- """
158
-
159
- # Generate sentiment analysis
160
- sentiment = self.sentiment_model(
161
- financial_context,
162
- truncation=True,
163
- max_length=512
164
- )[0]
165
-
166
- # Generate business analysis
167
- analysis_prompt = f"""[INST] As a financial analyst, provide a detailed analysis based on these financial metrics:
168
-
169
- {financial_context}
170
 
171
- Sentiment: {sentiment['label']} ({sentiment['score']:.2%})
172
 
173
- Please provide:
 
 
 
 
174
 
175
- 1. Business Status:
176
- - Financial health assessment
177
- - Growth trajectory
178
- - Key performance indicators analysis
179
 
180
- 2. Key Insights:
181
- - Revenue trends
182
- - Asset utilization
183
- - Financial efficiency metrics
184
- - Areas of concern or opportunity
 
 
 
 
185
 
186
- 3. Strategic Recommendations:
187
- - Specific action items based on the metrics
188
- - Growth opportunities
189
- - Risk mitigation strategies
190
- - Timeline-based roadmap
 
191
 
192
- Be specific and data-driven in your analysis.
193
- [/INST]"""
194
 
195
- response = self.analysis_model(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  analysis_prompt,
197
  max_length=1500,
198
- do_sample=False,
199
- num_return_sequences=1,
200
- truncation=True,
201
- pad_token_id=self.analysis_model.tokenizer.eos_token_id
202
- )
203
-
204
- return self.format_response(response[0]['generated_text'], sentiment)
205
-
 
 
206
  except Exception as e:
207
- logger.error(f"Error in analysis: {str(e)}")
208
- return f"Error generating analysis: {str(e)}"
209
 
210
- def format_response(self, analysis_text, sentiment):
211
- """Format the analysis response into structured sections"""
 
 
212
  try:
213
- # Split the analysis into sections
214
- sections = analysis_text.split('\n\n')
215
-
216
- # Initialize output sections
217
- status = []
218
- insights = []
219
- recommendations = []
220
-
221
- # Process each section
222
- current_section = None
223
- for section in sections:
224
- if "Business Status" in section:
225
- current_section = status
226
- elif "Key Insights" in section:
227
- current_section = insights
228
- elif "Strategic Recommendations" in section:
229
- current_section = recommendations
230
- elif current_section is not None:
231
- current_section.append(section.strip())
232
 
233
- # Format the final output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  output = [
235
- "# Financial Analysis Report\n\n",
236
- f"## Overall Sentiment: {sentiment['label'].upper()} ({sentiment['score']:.2%})\n\n",
237
- "## Business Status\n",
238
- "".join(f"- {item}\n" for item in status if item),
239
- "\n## Key Business Insights\n",
240
- "".join(f"- {item}\n" for item in insights if item),
241
- "\n## Strategic Recommendations & Roadmap\n",
242
- "".join(f"- {item}\n" for item in recommendations if item)
243
  ]
244
 
245
- return "".join(output)
246
 
247
  except Exception as e:
248
  logger.error(f"Error formatting response: {str(e)}")
@@ -254,22 +311,19 @@ def analyze_statements(income_statement, balance_sheet):
254
  if not income_statement or not balance_sheet:
255
  return "Please upload both Income Statement and Balance Sheet CSV files."
256
 
257
- # Validate files
258
- income_valid, income_msg = validate_financial_csv(income_statement, 'income_statement')
259
- if not income_valid:
260
- return f"Invalid Income Statement: {income_msg}"
261
-
262
- balance_valid, balance_msg = validate_financial_csv(balance_sheet, 'balance_sheet')
263
- if not balance_valid:
264
- return f"Invalid Balance Sheet: {balance_msg}"
265
-
266
- # Process if valid
267
  analyzer = FinancialAnalyzer()
268
- income_summary = analyzer.process_csv(income_statement)
269
- balance_summary = analyzer.process_csv(balance_sheet)
270
 
271
- result = analyzer.analyze_financials(income_summary, balance_summary)
 
 
 
272
  clear_gpu_memory()
 
273
  return result
274
 
275
  except Exception as e:
@@ -277,58 +331,167 @@ def analyze_statements(income_statement, balance_sheet):
277
  return f"""Analysis Error: {str(e)}
278
 
279
  Please ensure your CSV files:
280
- 1. Have proper headers (Revenue, Expenses, Profit for Income Statement)
281
- 2. Contain numeric data
282
- 3. Follow standard financial statement format
283
  4. Are not corrupted"""
284
 
285
- # Create Gradio interface
286
  iface = gr.Interface(
287
  fn=analyze_statements,
288
  inputs=[
289
  gr.File(
290
  label="Upload Income Statement (CSV)",
291
  file_types=[".csv"],
292
- file_count="single"
 
293
  ),
294
  gr.File(
295
  label="Upload Balance Sheet (CSV)",
296
  file_types=[".csv"],
297
- file_count="single"
 
 
 
 
 
 
 
298
  )
299
  ],
300
- outputs=gr.Markdown(),
301
- title="Financial Statement Analyzer",
302
- description="""## Financial Analysis Tool
303
-
304
- How to use:
305
- 1. Prepare your CSV files with proper headers:
306
- - Income Statement: Revenue, Expenses, Profit
307
- - Balance Sheet: Assets, Liabilities, Equity
308
- 2. Upload both files using the buttons below
309
- 3. Wait for the analysis to complete
310
-
311
- The tool will provide:
312
- - Business Status Assessment
313
- - Key Financial Insights
314
- - Strategic Recommendations
315
-
316
- Requirements:
317
- - Files must be in CSV format
318
- - Must contain numeric data
319
- - Standard financial statement format required""",
320
- flagging_mode="never"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  )
322
 
323
- # Launch the interface
324
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  try:
326
- iface.queue()
 
 
 
 
 
 
 
327
  iface.launch(
328
- share=False,
329
  server_name="0.0.0.0",
330
- server_port=7860
 
 
 
 
 
 
 
 
 
 
 
331
  )
 
332
  except Exception as e:
333
  logger.error(f"Launch error: {str(e)}")
334
- sys.exit(1)
 
 
 
 
 
 
 
 
 
 
 
3
  import pandas as pd
4
  import torch
5
  import logging
 
6
  import gc
7
+ import re
8
+ from transformers import pipeline
9
+ from typing import Dict, List, Tuple, Optional
10
 
11
  # Setup logging
12
  logging.basicConfig(
 
19
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
20
  logger.info(f"Using device: {DEVICE}")
21
 
22
+ class FinancialDataExtractor:
23
+ """Extracts and processes financial data from raw CSV files"""
24
+
25
+ def __init__(self):
26
+ self.numeric_pattern = re.compile(r'[^\d.-]+')
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ def clean_number(self, value: str) -> float:
29
+ """Clean numeric values from financial statements"""
30
+ try:
31
+ if pd.isna(value) or value == '' or value == '-':
32
+ return 0.0
33
+ if isinstance(value, (int, float)):
34
+ return float(value)
35
+
36
+ # Remove currency symbols, spaces, commas
37
+ cleaned = str(value).replace('$', '').replace(',', '').strip()
38
+ # Handle parentheses for negative numbers
39
+ if '(' in cleaned and ')' in cleaned:
40
+ cleaned = '-' + cleaned.replace('(', '').replace(')', '')
41
+ return float(cleaned)
42
+ except:
43
+ return 0.0
44
+
45
+ def process_income_statement(self, df: pd.DataFrame) -> Dict:
46
+ """Process income statement data"""
47
+ metrics = {}
48
+ years = [col for col in df.columns if str(col).isdigit()]
49
 
50
+ for year in years:
51
+ metrics[year] = {
52
+ 'Revenue': 0,
53
+ 'Expenses': 0,
54
+ 'Profit': 0,
55
+ 'Details': {}
56
+ }
57
+
58
+ # Extract revenue
59
+ total_revenue = df[df.iloc[:, 0].str.contains('Total Net Revenue|Revenue', na=False, regex=True)].iloc[0][year]
60
+ metrics[year]['Revenue'] = self.clean_number(total_revenue)
61
+
62
+ # Extract expenses
63
+ total_expenses = df[df.iloc[:, 0].str.contains('Total Expenses', na=False)].iloc[0][year]
64
+ metrics[year]['Expenses'] = self.clean_number(total_expenses)
65
+
66
+ # Extract profit
67
+ net_income = df[df.iloc[:, 0].str.contains('Net Income|Net Earnings', na=False, regex=True)].iloc[-1][year]
68
+ metrics[year]['Profit'] = self.clean_number(net_income)
69
+
70
+ # Extract additional details
71
+ metrics[year]['Details'] = {
72
+ 'COGS': self.clean_number(df[df.iloc[:, 0].str.contains('Cost of Goods Sold', na=False)].iloc[0][year]),
73
+ 'Gross_Profit': self.clean_number(df[df.iloc[:, 0].str.contains('Gross Profit', na=False)].iloc[0][year]),
74
+ 'Operating_Expenses': self.clean_number(total_expenses),
75
+ 'EBIT': self.clean_number(df[df.iloc[:, 0].str.contains('Earnings Before Interest & Taxes', na=False)].iloc[0][year]),
76
+ 'Interest_Expense': self.clean_number(df[df.iloc[:, 0].str.contains('Interest Expense', na=False)].iloc[0][year]),
77
+ 'Tax_Expense': self.clean_number(df[df.iloc[:, 0].str.contains('Income Taxes', na=False)].iloc[0][year])
78
+ }
79
+
80
+ return metrics
81
+
82
+ def process_balance_sheet(self, df: pd.DataFrame) -> Dict:
83
+ """Process balance sheet data"""
84
+ metrics = {}
85
+ years = [col for col in df.columns if str(col).isdigit()]
86
 
87
+ for year in years:
88
+ metrics[year] = {
89
+ 'Assets': 0,
90
+ 'Liabilities': 0,
91
+ 'Equity': 0,
92
+ 'Details': {}
93
+ }
94
+
95
+ # Extract main metrics
96
+ metrics[year]['Assets'] = self.clean_number(df[df.iloc[:, 0].str.contains('Total Assets', na=False)].iloc[0][year])
97
+ metrics[year]['Liabilities'] = self.clean_number(df[df.iloc[:, 0].str.contains('Total Liabilities', na=False)].iloc[0][year])
98
+ metrics[year]['Equity'] = self.clean_number(df[df.iloc[:, 0].str.contains("Shareholder's Equity", na=False)].iloc[-1][year])
99
+
100
+ # Extract additional details
101
+ metrics[year]['Details'] = {
102
+ 'Current_Assets': self.clean_number(df[df.iloc[:, 0].str.contains('Total current assets', na=False)].iloc[0][year]),
103
+ 'Fixed_Assets': self.clean_number(df[df.iloc[:, 0].str.contains('Property & Equipment', na=False)].iloc[0][year]),
104
+ 'Current_Liabilities': self.clean_number(df[df.iloc[:, 0].str.contains('Total current liabilities', na=False)].iloc[0][year]),
105
+ 'Long_Term_Debt': self.clean_number(df[df.iloc[:, 0].str.contains('Long-term debt', na=False)].iloc[0][year]),
106
+ 'Retained_Earnings': self.clean_number(df[df.iloc[:, 0].str.contains('Retained Earnings', na=False)].iloc[0][year])
107
+ }
108
+
109
+ return metrics
110
 
111
  class FinancialAnalyzer:
112
+ """Enhanced Financial Analyzer using small models"""
113
 
114
  def __init__(self):
115
+ self.extractor = FinancialDataExtractor()
116
  self.sentiment_model = None
117
  self.analysis_model = None
118
  self.load_models()
 
128
  truncation=True
129
  )
130
 
131
+ # Load small model for analysis
132
  self.analysis_model = pipeline(
133
  "text-generation",
134
  model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
 
140
  logger.error(f"Error loading models: {str(e)}")
141
  raise
142
 
143
+ def calculate_financial_ratios(self, income_metrics: Dict, balance_metrics: Dict, year: str) -> Dict:
144
+ """Calculate key financial ratios"""
145
  try:
146
+ income = income_metrics[year]
147
+ balance = balance_metrics[year]
 
 
 
148
 
149
+ ratios = {
150
+ 'Profitability': {
151
+ 'Gross_Margin': (income['Details']['Gross_Profit'] / income['Revenue']) * 100,
152
+ 'Operating_Margin': (income['Details']['EBIT'] / income['Revenue']) * 100,
153
+ 'Net_Margin': (income['Profit'] / income['Revenue']) * 100
154
+ },
155
+ 'Liquidity': {
156
+ 'Current_Ratio': balance['Details']['Current_Assets'] / balance['Details']['Current_Liabilities'],
157
+ 'Quick_Ratio': (balance['Details']['Current_Assets'] - 0) / balance['Details']['Current_Liabilities'],
158
+ 'Cash_Ratio': 0 # Would need cash balance for this
159
+ },
160
+ 'Solvency': {
161
+ 'Debt_to_Equity': balance['Liabilities'] / balance['Equity'],
162
+ 'Debt_Ratio': balance['Liabilities'] / balance['Assets'],
163
+ 'Interest_Coverage': income['Details']['EBIT'] / income['Details']['Interest_Expense'] if income['Details']['Interest_Expense'] != 0 else float('inf')
164
+ },
165
+ 'Efficiency': {
166
+ 'Asset_Turnover': income['Revenue'] / balance['Assets'],
167
+ 'ROE': income['Profit'] / balance['Equity'] * 100,
168
+ 'ROA': income['Profit'] / balance['Assets'] * 100
169
+ }
 
 
 
 
 
 
 
 
170
  }
171
 
172
+ return ratios
173
 
174
  except Exception as e:
175
+ logger.error(f"Error calculating ratios: {str(e)}")
176
+ return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
+ def generate_analysis_prompt(self, income_metrics: Dict, balance_metrics: Dict, ratios: Dict) -> str:
179
+ """Generate analysis prompt for LLM"""
180
+ latest_year = max(income_metrics.keys())
181
+ earliest_year = min(income_metrics.keys())
182
+
183
+ # Calculate growth metrics
184
+ revenue_growth = ((income_metrics[latest_year]['Revenue'] - income_metrics[earliest_year]['Revenue'])
185
+ / income_metrics[earliest_year]['Revenue'] * 100)
186
+ profit_growth = ((income_metrics[latest_year]['Profit'] - income_metrics[earliest_year]['Profit'])
187
+ / income_metrics[earliest_year]['Profit'] * 100)
188
+ asset_growth = ((balance_metrics[latest_year]['Assets'] - balance_metrics[earliest_year]['Assets'])
189
+ / balance_metrics[earliest_year]['Assets'] * 100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
+ prompt = f"""[INST] As a financial analyst, provide a comprehensive analysis of this company:
192
 
193
+ Financial Performance ({earliest_year}-{latest_year}):
194
+ 1. Growth Metrics:
195
+ - Revenue Growth: {revenue_growth:.1f}%
196
+ - Profit Growth: {profit_growth:.1f}%
197
+ - Asset Growth: {asset_growth:.1f}%
198
 
199
+ 2. Latest Year Performance ({latest_year}):
200
+ - Revenue: ${income_metrics[latest_year]['Revenue']:,.0f}
201
+ - Net Profit: ${income_metrics[latest_year]['Profit']:,.0f}
202
+ - Total Assets: ${balance_metrics[latest_year]['Assets']:,.0f}
203
 
204
+ 3. Key Ratios ({latest_year}):
205
+ - Profitability:
206
+ * Gross Margin: {ratios['Profitability']['Gross_Margin']:.1f}%
207
+ * Operating Margin: {ratios['Profitability']['Operating_Margin']:.1f}%
208
+ * Net Margin: {ratios['Profitability']['Net_Margin']:.1f}%
209
+ - Financial Health:
210
+ * Current Ratio: {ratios['Liquidity']['Current_Ratio']:.2f}
211
+ * Debt-to-Equity: {ratios['Solvency']['Debt_to_Equity']:.2f}
212
+ * ROE: {ratios['Efficiency']['ROE']:.1f}%
213
 
214
+ Provide:
215
+ 1. Overall financial health assessment
216
+ 2. Key performance insights and trends
217
+ 3. Risk analysis and concerns
218
+ 4. Specific strategic recommendations
219
+ [/INST]"""
220
 
221
+ return prompt
 
222
 
223
+ def analyze_financials(self, income_df: pd.DataFrame, balance_df: pd.DataFrame) -> str:
224
+ """Generate complete financial analysis"""
225
+ try:
226
+ # Extract metrics
227
+ income_metrics = self.extractor.process_income_statement(income_df)
228
+ balance_metrics = self.extractor.process_balance_sheet(balance_df)
229
+
230
+ # Calculate ratios for latest year
231
+ latest_year = max(income_metrics.keys())
232
+ ratios = self.calculate_financial_ratios(income_metrics, balance_metrics, latest_year)
233
+
234
+ # Generate analysis prompt
235
+ analysis_prompt = self.generate_analysis_prompt(income_metrics, balance_metrics, ratios)
236
+
237
+ # Get sentiment analysis
238
+ sentiment = self.sentiment_model(
239
+ analysis_prompt[:512],
240
+ truncation=True
241
+ )[0]
242
+
243
+ # Generate analysis
244
+ analysis = self.analysis_model(
245
  analysis_prompt,
246
  max_length=1500,
247
+ do_sample=True,
248
+ temperature=0.7,
249
+ num_return_sequences=1
250
+ )[0]['generated_text']
251
+
252
+ # Format the output
253
+ output = self.format_response(analysis, sentiment, income_metrics, balance_metrics, ratios, latest_year)
254
+
255
+ return output
256
+
257
  except Exception as e:
258
+ logger.error(f"Analysis error: {str(e)}")
259
+ return f"Error in analysis: {str(e)}"
260
 
261
+ def format_response(self, analysis: str, sentiment: Dict,
262
+ income_metrics: Dict, balance_metrics: Dict,
263
+ ratios: Dict, year: str) -> str:
264
+ """Format the analysis response"""
265
  try:
266
+ # Split analysis into sections
267
+ sections = analysis.split('\n\n')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ # Format metrics section
270
+ metrics_section = f"""
271
+ ### Key Financial Metrics ({year})
272
+ - Revenue: ${income_metrics[year]['Revenue']:,.0f}
273
+ - Net Profit: ${income_metrics[year]['Profit']:,.0f}
274
+ - Total Assets: ${balance_metrics[year]['Assets']:,.0f}
275
+
276
+ ### Financial Ratios
277
+ - Profitability:
278
+ * Gross Margin: {ratios['Profitability']['Gross_Margin']:.1f}%
279
+ * Operating Margin: {ratios['Profitability']['Operating_Margin']:.1f}%
280
+ * Net Margin: {ratios['Profitability']['Net_Margin']:.1f}%
281
+
282
+ - Financial Health:
283
+ * Current Ratio: {ratios['Liquidity']['Current_Ratio']:.2f}
284
+ * Debt-to-Equity: {ratios['Solvency']['Debt_to_Equity']:.2f}
285
+ * Interest Coverage: {ratios['Solvency']['Interest_Coverage']:.2f}
286
+
287
+ - Efficiency:
288
+ * ROE: {ratios['Efficiency']['ROE']:.1f}%
289
+ * ROA: {ratios['Efficiency']['ROA']:.1f}%
290
+ * Asset Turnover: {ratios['Efficiency']['Asset_Turnover']:.2f}
291
+ """
292
+
293
+ # Combine all sections
294
  output = [
295
+ "# Financial Analysis Report\n",
296
+ f"## Overall Sentiment: {sentiment['label'].upper()} ({sentiment['score']:.1%})\n",
297
+ metrics_section,
298
+ "\n## Analysis\n",
299
+ analysis
 
 
 
300
  ]
301
 
302
+ return "\n".join(output)
303
 
304
  except Exception as e:
305
  logger.error(f"Error formatting response: {str(e)}")
 
311
  if not income_statement or not balance_sheet:
312
  return "Please upload both Income Statement and Balance Sheet CSV files."
313
 
314
+ # Read files
315
+ income_df = pd.read_csv(income_statement.name)
316
+ balance_df = pd.read_csv(balance_sheet.name)
317
+
318
+ # Create analyzer instance
 
 
 
 
 
319
  analyzer = FinancialAnalyzer()
 
 
320
 
321
+ # Generate analysis
322
+ result = analyzer.analyze_financials(income_df, balance_df)
323
+
324
+ # Clear memory
325
  clear_gpu_memory()
326
+
327
  return result
328
 
329
  except Exception as e:
 
331
  return f"""Analysis Error: {str(e)}
332
 
333
  Please ensure your CSV files:
334
+ 1. Contain recognizable financial metrics
335
+ 2. Have clear period/year columns
336
+ 3. Use consistent number formatting
337
  4. Are not corrupted"""
338
 
339
+ # Create Gradio interface with enhanced features
340
  iface = gr.Interface(
341
  fn=analyze_statements,
342
  inputs=[
343
  gr.File(
344
  label="Upload Income Statement (CSV)",
345
  file_types=[".csv"],
346
+ file_count="single",
347
+ info="Upload your income statement in CSV format"
348
  ),
349
  gr.File(
350
  label="Upload Balance Sheet (CSV)",
351
  file_types=[".csv"],
352
+ file_count="single",
353
+ info="Upload your balance sheet in CSV format"
354
+ )
355
+ ],
356
+ outputs=[
357
+ gr.Markdown(
358
+ label="Financial Analysis Report",
359
+ show_label=True
360
  )
361
  ],
362
+ title="Advanced Financial Statement Analyzer",
363
+ description="""## Professional Financial Analysis Tool
364
+
365
+ ### Key Features:
366
+ - Comprehensive financial analysis using AI
367
+ - Sentiment analysis of financial performance
368
+ - Key ratio calculations and trend analysis
369
+ - Strategic recommendations
370
+
371
+ ### Supported Financial Statement Formats:
372
+
373
+ #### Income Statement should include:
374
+ - Revenue/Sales information
375
+ - Cost and Expense details
376
+ - Profit/Income figures
377
+ - Operating metrics
378
+ - Period/Year information
379
+
380
+ #### Balance Sheet should include:
381
+ - Asset information (Current & Non-current)
382
+ - Liability details (Current & Long-term)
383
+ - Equity components
384
+ - Period/Year information
385
+
386
+ ### Analysis Output:
387
+ 1. Overall Financial Health Assessment
388
+ 2. Key Performance Metrics & Ratios
389
+ 3. Trend Analysis
390
+ 4. Risk Assessment
391
+ 5. Strategic Recommendations
392
+ """,
393
+ article="""### Usage Tips:
394
+ 1. Ensure your CSV files are properly formatted
395
+ 2. Data should be in chronological order
396
+ 3. Numbers can include commas and currency symbols
397
+ 4. Negative numbers can be in parentheses
398
+ 5. Common headers will be automatically recognized
399
+
400
+ ### About the Analysis:
401
+ - Uses FinBERT for sentiment analysis
402
+ - Employs TinyLLama for strategic insights
403
+ - Calculates over 15 financial ratios
404
+ - Provides actionable recommendations
405
+ """,
406
+ examples=[
407
+ ["sample_income_statement.csv", "sample_balance_sheet.csv"]
408
+ ],
409
+ theme=gr.themes.Soft(
410
+ primary_hue="blue",
411
+ secondary_hue="purple",
412
+ ),
413
+ css="""
414
+ .gradio-container {
415
+ font-family: 'IBM Plex Sans', sans-serif;
416
+ }
417
+ .gr-button {
418
+ color: white;
419
+ border-radius: 8px;
420
+ background: linear-gradient(45deg, #1f6feb, #4688f1);
421
+ }
422
+ .gr-button:hover {
423
+ background: linear-gradient(45deg, #1856c5, #3b78e7);
424
+ }
425
+ .gr-form {
426
+ border-radius: 12px;
427
+ background: white;
428
+ padding: 24px;
429
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
430
+ }
431
+ .gr-box {
432
+ border-radius: 8px;
433
+ }
434
+ """
435
  )
436
 
437
+ # Add custom error handling
438
+ def custom_error_handler(err):
439
+ logger.error(f"Application error: {str(err)}")
440
+ return """### Error in Analysis
441
+
442
+ An error occurred while processing your financial statements. Please check:
443
+ 1. File format is correct CSV
444
+ 2. Contains required financial metrics
445
+ 3. Data is properly formatted
446
+ 4. No corrupted or missing values
447
+
448
+ If the problem persists, try:
449
+ - Removing any special formatting
450
+ - Ensuring consistent date/period format
451
+ - Checking for missing required fields
452
+
453
+ Error details: {str(err)}
454
+ """
455
+
456
+ iface.error_handler = custom_error_handler
457
+
458
+ # Launch configurations
459
+ def main():
460
  try:
461
+ # Setup memory management
462
+ gc.enable()
463
+
464
+ # Configure Gradio
465
+ gr.close_all()
466
+
467
+ # Launch with custom configurations
468
+ iface.queue(concurrency_count=1)
469
  iface.launch(
 
470
  server_name="0.0.0.0",
471
+ server_port=7860,
472
+ share=False,
473
+ enable_queue=True,
474
+ max_threads=4,
475
+ auth=None, # Add authentication if needed
476
+ ssl_keyfile=None, # Add SSL if needed
477
+ ssl_certfile=None,
478
+ ssl_keyfile_password=None,
479
+ show_error=True,
480
+ show_tips=True,
481
+ analytics_enabled=False,
482
+ debug=False
483
  )
484
+
485
  except Exception as e:
486
  logger.error(f"Launch error: {str(e)}")
487
+ raise
488
+
489
+ if __name__ == "__main__":
490
+ try:
491
+ main()
492
+ except KeyboardInterrupt:
493
+ logger.info("Shutting down gracefully...")
494
+ gr.close_all()
495
+ except Exception as e:
496
+ logger.error(f"Fatal error: {str(e)}")
497
+ sys.exit(1)