Shivam4096 commited on
Commit
75aa22f
Β·
verified Β·
1 Parent(s): 9f1a638

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. web_app/app.py +219 -203
web_app/app.py CHANGED
@@ -221,127 +221,116 @@ username = st.sidebar.text_input("Username / Email", value="", placeholder="name
221
  st.markdown("<h1 class='dashboard-title'>πŸ“ˆ Financial Statement Analyzer</h1>", unsafe_allow_html=True)
222
  st.markdown("<p class='dashboard-subtitle'>Multi-model AI insights scraper and report compilation portal</p>", unsafe_allow_html=True)
223
 
224
- # Left Column: Inputs
225
- # Right Column: Exporters & Progress
226
- col1, col2 = st.columns([1, 1.2])
227
-
228
- with col1:
229
- st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
230
- st.subheader("Analysis Parameters")
231
-
232
- # ── Ticker Search & Suggestion Logic ──
233
- search_col1, search_col2 = st.columns([3, 1])
234
- with search_col1:
235
- search_query = st.text_input("Search Company Name or Ticker", placeholder="e.g. TCS, Reliance, AAPL, MSFT")
236
- with search_col2:
237
- st.markdown("<div style='margin-top:28px;'></div>", unsafe_allow_html=True)
238
- search_clicked = st.button("πŸ” Find", use_container_width=True)
239
-
240
- ticker_input = search_query # default if no suggestions
241
-
242
- if search_clicked and search_query:
243
- with st.spinner("Searching..."):
244
- try:
245
- import yahooquery as yf
246
- res = yf.Search(search_query, max_results=8)
247
- quotes = [q for q in res.quotes if q.get("quoteType") in ("EQUITY", "MUTUALFUND", "ETF")]
248
- st.session_state["ticker_suggestions"] = quotes
249
- except Exception:
250
- st.session_state["ticker_suggestions"] = []
251
-
252
- if "ticker_suggestions" in st.session_state and st.session_state["ticker_suggestions"]:
253
- options = {}
254
- for q in st.session_state["ticker_suggestions"]:
255
- name = q.get('longname') or q.get('shortname') or q.get('symbol')
256
- label = f"{name} ({q['symbol']}) - {q.get('exchange', 'Unknown')}"
257
- options[label] = q['symbol']
258
-
259
- options[f"Use exact input: '{search_query}'"] = search_query
260
- selected_label = st.selectbox("Confirm Company:", list(options.keys()))
261
- ticker_input = options[selected_label]
262
-
263
- col_years, col_refresh = st.columns([1, 1])
264
- with col_years:
265
- analysis_years = st.selectbox("Analysis Horizon", options=[5, 7, 10], index=0)
266
- with col_refresh:
267
- force_refresh = st.checkbox("Bypass Cache (Force Scrape)", value=False)
268
-
269
- st.markdown("<b>Download Formats:</b>", unsafe_allow_html=True)
270
- chk_excel = st.checkbox("Excel Analysis Workbook (.xlsx)", value=True)
271
- chk_word = st.checkbox("Word Summary Report (.docx)", value=True)
272
- chk_pptx = st.checkbox("PowerPoint Pitch Deck (.pptx)", value=True)
273
-
274
- # ── AI Engine selection β€” auto-disable if key not supplied
275
- st.markdown("<b>AI Engines:</b>", unsafe_allow_html=True)
276
-
277
- _hint = "<span style='font-size:0.72rem;color:#9CA3AF;'>(add key in sidebar β†’)</span>"
278
-
279
- engines = []
280
-
281
- if st.checkbox(
282
- "πŸ†“ NVIDIA NIM β€” Llama 3.1 405B",
283
- value=bool(nvidia_key),
284
- disabled=not nvidia_key,
285
- help="Free credits. Get key at build.nvidia.com"
286
- ):
287
- engines.append("nvidia")
288
-
289
- if st.checkbox(
290
- "πŸ†“ Groq β€” Llama 3.3 70B",
291
- value=bool(groq_key),
292
- disabled=not groq_key,
293
- help="Free. Get key at console.groq.com"
294
- ):
295
- engines.append("groq")
296
 
297
- if st.checkbox(
298
- "πŸ†“ Hugging Face β€” DeepSeek-R1",
299
- value=bool(hf_key),
300
- disabled=not hf_key,
301
- help="Free via Hugging Face Inference API"
302
- ):
303
- engines.append("huggingface")
304
-
305
- if st.checkbox(
306
- "πŸ†“ Gemini 2.0 Flash (Google)",
307
- value=bool(gemini_key),
308
- disabled=not gemini_key,
309
- help="Free tier. Get key at aistudio.google.com"
310
- ):
311
- engines.append("gemini")
312
-
313
- if st.checkbox(
314
- "πŸ’³ Claude Haiku (Anthropic)",
315
- value=False,
316
- disabled=not claude_key,
317
- help="Paid. Add key in sidebar to enable."
318
- ):
319
- engines.append("claude")
320
-
321
- if st.checkbox(
322
- "πŸ’³ ChatGPT β€” GPT-4o mini (OpenAI)",
323
- value=False,
324
- disabled=not openai_key,
325
- help="Paid. Add key in sidebar to enable."
326
- ):
327
- engines.append("openai")
328
-
329
- if not groq_key and not gemini_key:
330
- st.info("πŸ’‘ Add a **free Groq key** in the sidebar to enable AI narratives.", icon="πŸ”‘")
331
-
332
- st.markdown("<br>", unsafe_allow_html=True)
333
- start_btn = st.button("Begin Financial Scrape & Analysis")
334
- st.markdown("</div>", unsafe_allow_html=True)
335
-
336
- with col2:
337
- st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
338
- st.subheader("Execution Status & Logs")
339
 
340
- log_container = st.empty()
341
- download_container = st.empty()
342
-
343
- log_container.code("Ready to analyze. Input a company and click the button above.", language="text")
344
- st.markdown("</div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
  # ── Analysis Runner ───────────────────────────────────────────────────
347
  if start_btn:
@@ -405,94 +394,121 @@ if start_btn:
405
  failed_validation = True
406
 
407
  if not failed_validation:
408
- logs = []
409
-
410
- def log_fn(msg):
411
- logs.append(msg)
412
- log_container.code("\n".join(logs), language="text")
413
-
414
  try:
415
- log_fn("="*60)
416
- log_fn(f"Starting analysis for: '{ticker_input}'")
417
- log_fn(f"Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
418
- log_fn(f"Formats: " + ", ".join([f for f, ok in [("Excel", chk_excel), ("Word", chk_word), ("PowerPoint", chk_pptx)] if ok]))
419
- log_fn(f"AI Models: {engines or ['None (Data Only)']}")
420
- log_fn("="*60)
421
-
422
- # 1. Resolve Company Info
423
- log_fn("\n[1/7] Resolving company metadata …")
424
- meta = resolve(ticker_input, log_fn=log_fn)
425
- log_fn(f" βœ“ {meta.name} | {meta.ticker} | {meta.exchange}")
426
-
427
- # 2. Fetch Statements
428
- log_fn("\n[2/7] Scrape financial statements …")
429
- fin_data = fetch_all(meta, force_refresh=force_refresh, log_fn=log_fn)
430
-
431
- # 3. Peers Comparison Selection
432
- log_fn("\n[3/7] Benchmarking peer selection …")
433
- fin_data["peers"] = select_peers(meta, log_fn=log_fn)
434
-
435
- # 4. Math ratios calculations
436
- log_fn("\n[4/7] Computing financial ratios & DuPont decomposition …")
437
- fin_data = calculate(fin_data, meta.to_dict())
438
- log_fn(" βœ“ Completed ratio math computations")
439
-
440
- # 5. AI Synthesis Narrative
441
- log_fn("\n[5/7] Requesting AI Model narrative generation …")
442
- narrative = generate_narrative(fin_data, meta.to_dict(), engines, log_fn=log_fn)
443
- fin_data.update(narrative)
444
-
445
- # 6. Generate outputs
446
- log_fn("\n[6/7] Building output report files …")
447
- fin_data["company"] = {
448
- "name": meta.name, "ticker": meta.ticker, "exchange": meta.exchange,
449
- "currency": fin_data.get("currency", meta.currency),
450
- "units": fin_data.get("units", "millions"),
451
- "fiscal_year_end": "",
452
- "industry": meta.industry, "sector": meta.sector,
453
- "business_description": meta.description,
454
- "report_date": datetime.date.today().isoformat(),
455
- "analysis_period_years": len(fin_data.get("years", [])),
456
- "share_price": meta.share_price, "market_cap": meta.mkt_cap,
457
- }
458
- fin_data["models_used"] = engines
459
- fin_data.setdefault("sources", [])
460
-
461
- ticker_safe = meta.ticker.replace(".", "_").replace("/", "_")
462
- timestamp_suffix = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
463
-
464
- # Exporters path settings
465
- excel_bytes = None
466
- word_bytes = None
467
- pptx_bytes = None
468
-
469
- out_dir = PARENT_DIR / "outputs"
470
- os.makedirs(str(out_dir), exist_ok=True)
471
-
472
- # Build files in memory / disk
473
- if chk_excel:
474
- xlsx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.xlsx"
475
- build_workbook(fin_data, str(xlsx_path))
476
- with open(xlsx_path, "rb") as f:
477
- excel_bytes = f.read()
478
- log_fn(f" βœ“ Created Excel analysis: {xlsx_path.name}")
479
 
480
- if chk_word:
481
- docx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.docx"
482
- build_document(fin_data, str(docx_path))
483
- with open(docx_path, "rb") as f:
484
- word_bytes = f.read()
485
- log_fn(f" βœ“ Created Word summary: {docx_path.name}")
486
 
487
- if chk_pptx:
488
- pptx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.pptx"
489
- build_presentation(fin_data, str(pptx_path))
490
- with open(pptx_path, "rb") as f:
491
- pptx_bytes = f.read()
492
- log_fn(f" βœ“ Created PowerPoint Presentation: {pptx_path.name}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
 
494
- log_fn(f"\n[7/7] βœ… Financial analysis process complete!")
495
- log_fn("="*60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
 
497
  # ── Generate download buttons in UI
498
  with download_container.container():
 
221
  st.markdown("<h1 class='dashboard-title'>πŸ“ˆ Financial Statement Analyzer</h1>", unsafe_allow_html=True)
222
  st.markdown("<p class='dashboard-subtitle'>Multi-model AI insights scraper and report compilation portal</p>", unsafe_allow_html=True)
223
 
224
+ st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
225
+ st.subheader("Analysis Parameters")
226
+
227
+ # ── Ticker Search & Suggestion Logic ──
228
+ search_col1, search_col2 = st.columns([3, 1])
229
+ with search_col1:
230
+ search_query = st.text_input("Search Company Name or Ticker", placeholder="e.g. TCS, Reliance, AAPL, MSFT")
231
+ with search_col2:
232
+ st.markdown("<div style='margin-top:28px;'></div>", unsafe_allow_html=True)
233
+ search_clicked = st.button("πŸ” Find", use_container_width=True)
234
+
235
+ ticker_input = search_query # default if no suggestions
236
+
237
+ if search_clicked and search_query:
238
+ with st.spinner("Searching..."):
239
+ try:
240
+ import yahooquery as yq
241
+ res = yq.search(search_query)
242
+ quotes = [q for q in res.get("quotes", []) if q.get("quoteType") in ("EQUITY", "MUTUALFUND", "ETF")]
243
+ st.session_state["ticker_suggestions"] = quotes
244
+ except Exception:
245
+ st.session_state["ticker_suggestions"] = []
246
+
247
+ if "ticker_suggestions" in st.session_state and st.session_state["ticker_suggestions"]:
248
+ options = {}
249
+ for q in st.session_state["ticker_suggestions"]:
250
+ name = q.get('longname') or q.get('shortname') or q.get('symbol')
251
+ label = f"{name} ({q['symbol']}) - {q.get('exchange', 'Unknown')}"
252
+ options[label] = q['symbol']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
+ options[f"Use exact input: '{search_query}'"] = search_query
255
+ selected_label = st.selectbox("Confirm Company:", list(options.keys()))
256
+ ticker_input = options[selected_label]
257
+
258
+ col_years, col_refresh = st.columns([1, 1])
259
+ with col_years:
260
+ analysis_years = st.selectbox("Analysis Horizon", options=[5, 7, 10], index=0)
261
+ with col_refresh:
262
+ force_refresh = st.checkbox("Bypass Cache (Force Scrape)", value=False)
263
+
264
+ st.markdown("<b>Download Formats:</b>", unsafe_allow_html=True)
265
+ chk_excel = st.checkbox("Excel Analysis Workbook (.xlsx)", value=True)
266
+ chk_word = st.checkbox("Word Summary Report (.docx)", value=True)
267
+ chk_pptx = st.checkbox("PowerPoint Pitch Deck (.pptx)", value=True)
268
+
269
+ # ── AI Engine selection β€” auto-disable if key not supplied
270
+ st.markdown("<b>AI Engines:</b>", unsafe_allow_html=True)
271
+
272
+ _hint = "<span style='font-size:0.72rem;color:#9CA3AF;'>(add key in sidebar β†’)</span>"
273
+
274
+ engines = []
275
+
276
+ if st.checkbox(
277
+ "πŸ†“ NVIDIA NIM β€” Llama 3.1 405B",
278
+ value=bool(nvidia_key),
279
+ disabled=not nvidia_key,
280
+ help="Free credits. Get key at build.nvidia.com"
281
+ ):
282
+ engines.append("nvidia")
283
+
284
+ if st.checkbox(
285
+ "πŸ†“ Groq β€” Llama 3.3 70B",
286
+ value=bool(groq_key),
287
+ disabled=not groq_key,
288
+ help="Free. Get key at console.groq.com"
289
+ ):
290
+ engines.append("groq")
 
 
 
 
 
291
 
292
+ if st.checkbox(
293
+ "πŸ†“ Hugging Face β€” DeepSeek-R1",
294
+ value=bool(hf_key),
295
+ disabled=not hf_key,
296
+ help="Free via Hugging Face Inference API"
297
+ ):
298
+ engines.append("huggingface")
299
+
300
+ if st.checkbox(
301
+ "πŸ†“ Gemini 2.0 Flash (Google)",
302
+ value=bool(gemini_key),
303
+ disabled=not gemini_key,
304
+ help="Free tier. Get key at aistudio.google.com"
305
+ ):
306
+ engines.append("gemini")
307
+
308
+ if st.checkbox(
309
+ "πŸ’³ Claude Haiku (Anthropic)",
310
+ value=False,
311
+ disabled=not claude_key,
312
+ help="Paid. Add key in sidebar to enable."
313
+ ):
314
+ engines.append("claude")
315
+
316
+ if st.checkbox(
317
+ "πŸ’³ ChatGPT β€” GPT-4o mini (OpenAI)",
318
+ value=False,
319
+ disabled=not openai_key,
320
+ help="Paid. Add key in sidebar to enable."
321
+ ):
322
+ engines.append("openai")
323
+
324
+ if not groq_key and not gemini_key:
325
+ st.info("πŸ’‘ Add a **free Groq key** in the sidebar to enable AI narratives.", icon="πŸ”‘")
326
+
327
+ st.markdown("<br>", unsafe_allow_html=True)
328
+ start_btn = st.button("Begin Financial Scrape & Analysis")
329
+ st.markdown("</div>", unsafe_allow_html=True)
330
+
331
+ progress_container = st.empty()
332
+ metrics_container = st.empty()
333
+ download_container = st.empty()
334
 
335
  # ── Analysis Runner ───────────────────────────────────────────────────
336
  if start_btn:
 
394
  failed_validation = True
395
 
396
  if not failed_validation:
 
 
 
 
 
 
397
  try:
398
+ progress_bar = progress_container.progress(0, text="Initializing analysis...")
399
+ with progress_container.status("Analysis Execution Status", expanded=True) as status_box:
400
+ def log_fn(msg):
401
+ status_box.write(msg)
402
+
403
+ log_fn(f"Starting analysis for: '{ticker_input}'")
404
+ log_fn(f"Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
405
+
406
+ # 1. Resolve Company Info
407
+ progress_bar.progress(14, text="[1/7] Resolving company metadata...")
408
+ log_fn("\n[1/7] Resolving company metadata …")
409
+ meta = resolve(ticker_input, log_fn=log_fn)
410
+ log_fn(f" βœ“ {meta.name} | {meta.ticker} | {meta.exchange}")
411
+
412
+ # 2. Fetch Statements
413
+ progress_bar.progress(28, text="[2/7] Scrape financial statements...")
414
+ log_fn("\n[2/7] Scrape financial statements …")
415
+ fin_data = fetch_all(meta, force_refresh=force_refresh, log_fn=log_fn)
416
+
417
+ # 3. Peers Comparison Selection
418
+ progress_bar.progress(42, text="[3/7] Benchmarking peer selection...")
419
+ log_fn("\n[3/7] Benchmarking peer selection …")
420
+ fin_data["peers"] = select_peers(meta, log_fn=log_fn)
421
+
422
+ # 4. Math ratios calculations
423
+ progress_bar.progress(57, text="[4/7] Computing financial ratios...")
424
+ log_fn("\n[4/7] Computing financial ratios & DuPont decomposition …")
425
+ fin_data = calculate(fin_data, meta.to_dict())
426
+ log_fn(" βœ“ Completed ratio math computations")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
 
428
+ # 5. AI Synthesis Narrative
429
+ progress_bar.progress(71, text="[5/7] Requesting AI Model narrative...")
430
+ log_fn("\n[5/7] Requesting AI Model narrative generation …")
431
+ narrative = generate_narrative(fin_data, meta.to_dict(), engines, log_fn=log_fn)
432
+ fin_data.update(narrative)
 
433
 
434
+ # 6. Generate outputs
435
+ progress_bar.progress(85, text="[6/7] Building output report files...")
436
+ log_fn("\n[6/7] Building output report files …")
437
+ fin_data["company"] = {
438
+ "name": meta.name, "ticker": meta.ticker, "exchange": meta.exchange,
439
+ "currency": fin_data.get("currency", meta.currency),
440
+ "units": fin_data.get("units", "millions"),
441
+ "fiscal_year_end": "",
442
+ "industry": meta.industry, "sector": meta.sector,
443
+ "business_description": meta.description,
444
+ "report_date": datetime.date.today().isoformat(),
445
+ "analysis_period_years": len(fin_data.get("years", [])),
446
+ "share_price": meta.share_price, "market_cap": meta.mkt_cap,
447
+ }
448
+ fin_data["models_used"] = engines
449
+ fin_data.setdefault("sources", [])
450
+
451
+ ticker_safe = meta.ticker.replace(".", "_").replace("/", "_")
452
+ timestamp_suffix = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
453
+
454
+ # Exporters path settings
455
+ excel_bytes = None
456
+ word_bytes = None
457
+ pptx_bytes = None
458
+
459
+ out_dir = PARENT_DIR / "outputs"
460
+ os.makedirs(str(out_dir), exist_ok=True)
461
+
462
+ # Build files in memory / disk
463
+ if chk_excel:
464
+ xlsx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.xlsx"
465
+ build_workbook(fin_data, str(xlsx_path))
466
+ with open(xlsx_path, "rb") as f:
467
+ excel_bytes = f.read()
468
+ log_fn(f" βœ“ Created Excel analysis: {xlsx_path.name}")
469
+
470
+ if chk_word:
471
+ docx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.docx"
472
+ build_document(fin_data, str(docx_path))
473
+ with open(docx_path, "rb") as f:
474
+ word_bytes = f.read()
475
+ log_fn(f" βœ“ Created Word summary: {docx_path.name}")
476
+
477
+ if chk_pptx:
478
+ pptx_path = out_dir / f"{ticker_safe}_financial_analysis_{timestamp_suffix}.pptx"
479
+ build_presentation(fin_data, str(pptx_path))
480
+ with open(pptx_path, "rb") as f:
481
+ pptx_bytes = f.read()
482
+ log_fn(f" βœ“ Created PowerPoint Presentation: {pptx_path.name}")
483
+
484
+ progress_bar.progress(100, text="[7/7] Financial analysis process complete!")
485
+ log_fn(f"\n[7/7] βœ… Financial analysis process complete!")
486
+ status_box.update(state="complete", expanded=False)
487
 
488
+ # ── Display Success Metrics
489
+ st.toast('Analysis successfully completed!', icon='πŸŽ‰')
490
+ with metrics_container.container():
491
+ st.markdown("### πŸ“Š Quick Insights", unsafe_allow_html=True)
492
+ mc1, mc2, mc3 = st.columns(3)
493
+ mc1.metric("Company", meta.name, meta.ticker)
494
+
495
+ # Format Price
496
+ price_val = meta.share_price
497
+ price_str = f"{fin_data.get('currency', '')} {price_val:,.2f}" if price_val else "N/A"
498
+ mc2.metric("Share Price", price_str)
499
+
500
+ # Format Market Cap
501
+ mc_val = meta.mkt_cap
502
+ mc_str = "N/A"
503
+ if mc_val:
504
+ if mc_val > 1e12:
505
+ mc_str = f"{fin_data.get('currency', '')} {mc_val/1e12:,.2f} Trillion"
506
+ elif mc_val > 1e9:
507
+ mc_str = f"{fin_data.get('currency', '')} {mc_val/1e9:,.2f} Billion"
508
+ else:
509
+ mc_str = f"{fin_data.get('currency', '')} {mc_val/1e6:,.2f} Million"
510
+ mc3.metric("Market Cap", mc_str)
511
+ st.markdown("<br>", unsafe_allow_html=True)
512
 
513
  # ── Generate download buttons in UI
514
  with download_container.container():