|
|
"""UI component functions for the financial dashboard.""" |
|
|
|
|
|
import streamlit as st |
|
|
import pandas as pd |
|
|
from data import format_financial_value, get_profitability_metrics |
|
|
|
|
|
|
|
|
def display_price_metrics(metrics: dict): |
|
|
"""Display key price metrics in columns.""" |
|
|
st.markdown('<div class="section-title">π Price Metrics</div>', unsafe_allow_html=True) |
|
|
|
|
|
col1, col2, col3, col4 = st.columns(4) |
|
|
|
|
|
with col1: |
|
|
st.metric("Current Price", f"${metrics['current_price']:.2f}", |
|
|
f"{metrics['price_change']:+.2f}", delta_color="normal") |
|
|
|
|
|
with col2: |
|
|
st.metric("Day Change %", f"{metrics['price_change_pct']:+.2f}%", |
|
|
None, delta_color="normal") |
|
|
|
|
|
with col3: |
|
|
st.metric("52W High", f"${metrics['high_52w']:.2f}") |
|
|
|
|
|
with col4: |
|
|
st.metric("52W Low", f"${metrics['low_52w']:.2f}") |
|
|
|
|
|
|
|
|
def display_company_info(profile_info): |
|
|
"""Display company information.""" |
|
|
st.markdown('<div class="section-title">π Company Information</div>', unsafe_allow_html=True) |
|
|
|
|
|
if profile_info: |
|
|
info_col1, info_col2 = st.columns(2) |
|
|
with info_col1: |
|
|
st.write(f"**Company Name:** {getattr(profile_info, 'name', 'N/A')}") |
|
|
st.write(f"**Sector:** {getattr(profile_info, 'sector', 'N/A')}") |
|
|
st.write(f"**Industry:** {getattr(profile_info, 'industry', 'N/A')}") |
|
|
|
|
|
with info_col2: |
|
|
st.write(f"**Country:** {getattr(profile_info, 'country', 'N/A')}") |
|
|
st.write(f"**Exchange:** {getattr(profile_info, 'exchange', 'N/A')}") |
|
|
st.write(f"**Website:** {getattr(profile_info, 'website', 'N/A')}") |
|
|
|
|
|
|
|
|
def display_financial_metrics(income_stmt: pd.DataFrame): |
|
|
"""Display financial metrics from income statement.""" |
|
|
st.markdown('<div class="section-title">π° Financial Metrics</div>', unsafe_allow_html=True) |
|
|
|
|
|
latest_income = income_stmt.iloc[0] if len(income_stmt) > 0 else None |
|
|
|
|
|
if latest_income is not None: |
|
|
|
|
|
fin_col1, fin_col2, fin_col3, fin_col4 = st.columns(4) |
|
|
|
|
|
with fin_col1: |
|
|
revenue = latest_income.get('total_revenue', 0) |
|
|
if pd.notna(revenue) and revenue > 0: |
|
|
st.metric("Total Revenue", format_financial_value(revenue)) |
|
|
else: |
|
|
st.metric("Total Revenue", "N/A") |
|
|
|
|
|
with fin_col2: |
|
|
net_income = latest_income.get('net_income', 0) |
|
|
if pd.notna(net_income) and net_income > 0: |
|
|
st.metric("Net Income", format_financial_value(net_income)) |
|
|
else: |
|
|
st.metric("Net Income", "N/A") |
|
|
|
|
|
with fin_col3: |
|
|
gross_profit = latest_income.get('gross_profit', 0) |
|
|
if pd.notna(gross_profit) and gross_profit > 0: |
|
|
st.metric("Gross Profit", format_financial_value(gross_profit)) |
|
|
else: |
|
|
st.metric("Gross Profit", "N/A") |
|
|
|
|
|
with fin_col4: |
|
|
operating_income = latest_income.get('operating_income', 0) |
|
|
if pd.notna(operating_income) and operating_income > 0: |
|
|
st.metric("Operating Income", format_financial_value(operating_income)) |
|
|
else: |
|
|
st.metric("Operating Income", "N/A") |
|
|
|
|
|
|
|
|
fin_col5, fin_col6, fin_col7, fin_col8 = st.columns(4) |
|
|
|
|
|
with fin_col5: |
|
|
eps = latest_income.get('diluted_earnings_per_share', 0) |
|
|
if pd.notna(eps): |
|
|
st.metric("EPS (Diluted)", f"${eps:.2f}") |
|
|
else: |
|
|
st.metric("EPS (Diluted)", "N/A") |
|
|
|
|
|
with fin_col6: |
|
|
ebitda = latest_income.get('ebitda', 0) |
|
|
if pd.notna(ebitda) and ebitda > 0: |
|
|
st.metric("EBITDA", format_financial_value(ebitda)) |
|
|
else: |
|
|
st.metric("EBITDA", "N/A") |
|
|
|
|
|
with fin_col7: |
|
|
cogs = latest_income.get('cost_of_revenue', 0) |
|
|
if pd.notna(cogs) and cogs > 0: |
|
|
st.metric("Cost of Revenue", format_financial_value(cogs)) |
|
|
else: |
|
|
st.metric("Cost of Revenue", "N/A") |
|
|
|
|
|
with fin_col8: |
|
|
rd_expense = latest_income.get('research_and_development_expense', 0) |
|
|
if pd.notna(rd_expense) and rd_expense > 0: |
|
|
st.metric("R&D Expense", format_financial_value(rd_expense)) |
|
|
else: |
|
|
st.metric("R&D Expense", "N/A") |
|
|
|
|
|
|
|
|
def display_income_statement(income_stmt: pd.DataFrame): |
|
|
"""Display formatted income statement table.""" |
|
|
st.markdown("### Income Statement") |
|
|
|
|
|
if not income_stmt.empty: |
|
|
display_columns = [ |
|
|
'period_ending', |
|
|
'total_revenue', |
|
|
'cost_of_revenue', |
|
|
'gross_profit', |
|
|
'operating_income', |
|
|
'net_income', |
|
|
'diluted_earnings_per_share', |
|
|
'ebitda' |
|
|
] |
|
|
|
|
|
available_cols = [col for col in display_columns if col in income_stmt.columns] |
|
|
financial_display = income_stmt[available_cols].copy() |
|
|
|
|
|
for col in financial_display.columns: |
|
|
if col != 'period_ending': |
|
|
financial_display[col] = financial_display[col].apply( |
|
|
lambda x: format_financial_value(x) |
|
|
) |
|
|
|
|
|
st.dataframe(financial_display, use_container_width=True, hide_index=True) |
|
|
|
|
|
|
|
|
def display_profitability_metrics(income_stmt: pd.DataFrame): |
|
|
"""Display profitability metrics.""" |
|
|
st.markdown("### Profitability Metrics") |
|
|
|
|
|
prof_col1, prof_col2 = st.columns(2) |
|
|
latest_data = income_stmt.iloc[0] |
|
|
metrics = get_profitability_metrics(latest_data) |
|
|
|
|
|
with prof_col1: |
|
|
if "gross_margin" in metrics: |
|
|
st.metric("Gross Margin", f"{metrics['gross_margin']:.2f}%") |
|
|
if "net_margin" in metrics: |
|
|
st.metric("Net Profit Margin", f"{metrics['net_margin']:.2f}%") |
|
|
|
|
|
with prof_col2: |
|
|
if "operating_margin" in metrics: |
|
|
st.metric("Operating Margin", f"{metrics['operating_margin']:.2f}%") |
|
|
|
|
|
if len(income_stmt) > 1: |
|
|
prev_revenue = income_stmt.iloc[1].get('total_revenue', 0) |
|
|
total_rev = latest_data.get('total_revenue', 0) |
|
|
if prev_revenue and prev_revenue > 0: |
|
|
revenue_growth = ((total_rev - prev_revenue) / prev_revenue) * 100 |
|
|
st.metric("Revenue Growth (YoY)", f"{revenue_growth:+.2f}%") |
|
|
|