aboutsummaryrefslogtreecommitdiff
path: root/components/filings.py
blob: d366a2bd800a3a3598cfbab34d7d5bec57d2b22e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""SEC filings — recent 10-K, 10-Q, 8-K and other forms with direct links."""
import pandas as pd
import streamlit as st

from services.fmp_service import get_sec_filings


_FORM_DESCRIPTIONS = {
    "10-K": "Annual report",
    "10-Q": "Quarterly report",
    "8-K": "Material event disclosure",
    "DEF 14A": "Proxy statement",
    "S-1": "IPO registration",
    "S-3": "Securities registration",
    "4": "Insider ownership change",
    "SC 13G": "Beneficial ownership (passive)",
    "SC 13D": "Beneficial ownership (active)",
}

_FORM_COLORS = {
    "10-K": "rgba(79,142,247,0.15)",
    "10-Q": "rgba(130,224,170,0.15)",
    "8-K": "rgba(247,162,79,0.15)",
}


def _describe_form(form_type: str) -> str:
    return _FORM_DESCRIPTIONS.get(form_type.strip().upper(), "")


def render_filings(ticker: str):
    with st.spinner("Loading SEC filings…"):
        raw = get_sec_filings(ticker)

    if not raw:
        st.info("No SEC filing data available. Requires FMP API key.")
        return

    # Collect unique form types for filter
    form_types = sorted({str(r.get("type") or r.get("form") or "").strip() for r in raw if r.get("type") or r.get("form")})
    filter_options = ["All"] + [t for t in ["10-K", "10-Q", "8-K"] if t in form_types] + \
                     [t for t in form_types if t not in ("10-K", "10-Q", "8-K")]

    filter_col, _ = st.columns([1, 3])
    with filter_col:
        selected_type = st.selectbox("Form type", options=filter_options, index=0, key="filings_filter")

    # Summary counts
    counts = {}
    for r in raw:
        ft = str(r.get("type") or r.get("form") or "Other").strip()
        counts[ft] = counts.get(ft, 0) + 1

    priority_forms = ["10-K", "10-Q", "8-K"]
    summary_forms = [f for f in priority_forms if f in counts]
    if summary_forms:
        cols = st.columns(len(summary_forms))
        for col, ft in zip(cols, summary_forms):
            col.metric(ft, counts[ft])
    st.write("")

    # Filter
    if selected_type == "All":
        filtered = raw
    else:
        filtered = [
            r for r in raw
            if str(r.get("type") or r.get("form") or "").strip() == selected_type
        ]

    if not filtered:
        st.info("No filings match the current filter.")
        return

    # Build display table
    rows = []
    for item in filtered:
        form_type = str(item.get("type") or item.get("form") or "—").strip()
        date = str(item.get("filingDate") or item.get("date") or "")[:10]
        description = item.get("description") or _describe_form(form_type) or "—"
        url = item.get("link") or item.get("finalLink") or item.get("url") or ""
        rows.append({
            "Date": date,
            "Form": form_type,
            "Description": description,
            "Link": url,
        })

    df = pd.DataFrame(rows)

    # Render as clickable table — Streamlit doesn't natively support link columns,
    # so we render each row as a compact card for the most recent 30 entries.
    for _, row in df.head(30).iterrows():
        form = row["Form"]
        color = _FORM_COLORS.get(form, "rgba(255,255,255,0.05)")
        date = row["Date"]
        desc = row["Description"]
        link = row["Link"]

        with st.container():
            left, right = st.columns([5, 1])
            with left:
                st.markdown(
                    f"<div style='background:{color};padding:6px 10px;border-radius:4px;margin-bottom:2px'>"
                    f"<strong>{form}</strong> &nbsp;·&nbsp; <span style='color:#9aa0b0;font-size:0.82rem'>{date}</span><br>"
                    f"<span style='font-size:0.85rem'>{desc}</span>"
                    f"</div>",
                    unsafe_allow_html=True,
                )
            with right:
                if link:
                    st.markdown(
                        f"<div style='padding-top:8px'><a href='{link}' target='_blank'>🔗 View</a></div>",
                        unsafe_allow_html=True,
                    )