Files
All-in-one-Financial-Analysis/views/tab1_ai.py
T
shawnkim1997andClaude Sonnet 4.6 d337c63976 refactor: modular architecture v3.0 + SEC filing viewer fix + README
Architecture (3,909-line monolith → 28 focused modules, all < 300 lines):
- config/: constants.py (company lists, row maps, Damodaran baselines), theme.py (CSS/HTML)
- utils/: prefs, formatting, ticker, dcf, charts, ui_helpers
- data/: sec_parser, sec_fetcher, sec_downloader, financials, fundamentals,
         valuation, ratios, scores, scores_ai, market
- ai/: gemini_core, gemini_sec, gemini_insights
- views/: sidebar, tab1_quant, tab1_ai, tab1_filings, tab2_dcf,
          tab3_comps, tab4_news, tab5_markets, tab6_crypto, tab7_technical
- app.py: thin orchestrator (~118 lines)
- Strict unidirectional dependency graph (no circular imports)
- All @st.cache_data TTLs and st.session_state keys preserved identically

SEC filing viewer fix:
- Rebuilt EDGAR fetch chain: company_tickers.json → CIK → submissions API
  → filings.recent.primaryDocument[] (replaces deprecated directory.item)
- Filing type selectbox (10-K, 10-Q, 8-K, 20-F, 6-K) connected to backend
- Native HTML rendered via streamlit.components.v1.html() with CSS reset
- Errors surfaced explicitly with st.error()
- DART direct links restored for Korean-listed companies

.gitignore: data/ → data/*.json + data/*.html (preserve Python modules)
README: full rewrite for master's portfolio — 7-tab layout, architecture
diagram, modular structure tree, technical challenges, design rationale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:11:07 +00:00

127 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tab 1 — Deep-Dive AI Analysis: Management Strategy & Risk Factors buttons,
display of saved results."""
import streamlit as st
from data.sec_downloader import get_10k_sections
from data.fundamentals import get_sector_industry
from ai.gemini_core import _gemini_forensic_audit
from ai.gemini_sec import get_gemini_item7_strategy_stream, get_gemini_item1a_risks_stream
def render_tab1_ai_analysis(ticker, quant_ticker, market):
"""Render the Deep-Dive Analysis (AI) section of Tab 1."""
st.markdown("---")
st.markdown("#### 🔍 Deep-Dive Analysis (AI)")
st.caption("10-K sections are cached in **data/**; repeat runs use cache for instant AI analysis. First run may take 2060 s to fetch 10-K; Gemini then streams in ~510 s.")
if not ticker:
st.caption("Enter a ticker in the sidebar to enable analysis.")
else:
api_ok = bool(st.session_state.get("google_api_key"))
email_ok = bool(st.session_state.get("sec_email"))
err_msg = []
if not api_ok:
err_msg.append("Google API Key")
if not email_ok:
err_msg.append("SEC EDGAR Email")
if err_msg:
st.caption(f"Set **{' and '.join(err_msg)}** in the sidebar to run analysis.")
col_a, col_b = st.columns(2)
is_us = market and "US" in market
is_korea = market and ("Korea" in market or "KOSPI" in market or "KOSDAQ" in market)
is_japan_uk = market and ("Japan" in market or "Nikkei" in market or "UK" in market or "LSE" in market)
# --- Button A: Management Strategy ---
with col_a:
if st.button("Analyze Management Strategy (MD&A)", key="run_mda_strategy"):
if is_korea:
st.warning("DART API integration for Korean MD&A is currently under construction. Please check back in Phase 2.")
elif is_japan_uk:
st.warning("EDINET/LSE document parsing is currently under development.")
elif not api_ok or not email_ok:
st.error("Set API Key and SEC Email in the sidebar.")
else:
try:
with st.status("Loading 10-K (cache or download)...", expanded=True) as status:
sections, _ = get_10k_sections(ticker, st.session_state["sec_email"])
si = get_sector_industry(quant_ticker)
status.update(label="10-K loaded. Calling Gemini…", state="running")
# Stream OUTSIDE the status box so user sees text as it arrives
st.markdown("### Management Strategy (Item 7)")
st.caption("Streaming from Gemini (first words in ~510 sec, then flows in real time).")
stream_gen = get_gemini_item7_strategy_stream(
st.session_state["google_api_key"],
sections.get("item7") or "",
ticker,
si.get("sector") or "N/A",
si.get("industry") or "N/A",
)
# write_stream returns the full concatenated string after it finishes streaming
full_response = st.write_stream(stream_gen)
st.session_state["mda_strategy_result"] = full_response
st.session_state["mda_strategy_ticker"] = ticker
st.session_state["mda_strategy_error"] = None
except Exception as e:
st.session_state["mda_strategy_error"] = str(e)
st.error(f"Strategy analysis failed: {str(e)}")
# --- Button B: Risk Factors & Forensic ---
with col_b:
if st.button("Analyze Risk Factors (Item 1A)", key="run_mda_risk"):
if is_korea:
st.warning("DART API integration for Korean MD&A is currently under construction. Please check back in Phase 2.")
elif is_japan_uk:
st.warning("EDINET/LSE document parsing is currently under development.")
elif not api_ok or not email_ok:
st.error("Set API Key and SEC Email in the sidebar.")
else:
try:
with st.status("Loading 10-K (cache or download)...", expanded=True) as status:
sections, _ = get_10k_sections(ticker, st.session_state["sec_email"])
status.update(label="10-K loaded. Running forensic audit…", state="running")
# Run forensic silently IN THE BACKGROUND first
forensic = _gemini_forensic_audit(
st.session_state["google_api_key"],
sections.get("item3") or "",
sections.get("item9a") or "",
ticker,
)
status.update(label="Done.", state="complete")
# Stream the risk factors OUTSIDE the status box
st.markdown("### Risk Factors (Item 1A)")
st.caption("Streaming from Gemini (first words in ~510 sec, then flows in real time).")
stream_gen = get_gemini_item1a_risks_stream(
st.session_state["google_api_key"],
sections.get("item1a") or "",
ticker,
)
risk_response = st.write_stream(stream_gen)
# Combine both for the final result
final_out = risk_response
if forensic and forensic.strip():
st.markdown("### Forensic Audit (Item 3 & 9A)")
st.markdown(forensic.strip())
final_out += f"\n\n---\n\n### Forensic Audit (Item 3 & 9A)\n\n{forensic.strip()}"
st.session_state["mda_risk_result"] = final_out
st.session_state["mda_risk_ticker"] = ticker
st.session_state["mda_risk_error"] = None
except Exception as e:
st.session_state["mda_risk_error"] = str(e)
st.error(f"Risk analysis failed: {str(e)}")
# --- Display Saved Results if User Switches Tabs ---
st.markdown("---")
if st.session_state.get("mda_strategy_ticker") == ticker:
if st.session_state.get("mda_strategy_error"):
st.error("Strategy Error: " + st.session_state["mda_strategy_error"])
elif st.session_state.get("mda_strategy_result"):
with st.expander("View Previous Strategy Analysis", expanded=True):
st.markdown(st.session_state["mda_strategy_result"])
if st.session_state.get("mda_risk_ticker") == ticker:
if st.session_state.get("mda_risk_error"):
st.error("Risk Error: " + st.session_state["mda_risk_error"])
elif st.session_state.get("mda_risk_result"):
with st.expander("View Previous Risk & Forensic Analysis", expanded=True):
st.markdown(st.session_state["mda_risk_result"])