Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- 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 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
st.
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 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 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 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 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 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 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
log_fn(f" β Created Word summary: {docx_path.name}")
|
| 486 |
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
|
| 494 |
-
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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():
|