"""Gradio dashboard grid component for valuation charts. This module creates a responsive grid layout for displaying 8-9 fundamental charts using Gradio's Row and Column components with min_width for automatic wrapping. """ import logging from pathlib import Path from typing import List, Optional import gradio as gr from config.models import ValuationDashboard logger = logging.getLogger(__name__) class DashboardComponent: """Responsive dashboard grid for fundamental charts.""" def __init__(self): """Initialize dashboard component.""" logger.info("DashboardComponent initialized") def create_desktop_grid(self) -> List[gr.Image]: """Create 2x4 desktop grid layout with responsive wrapping. Uses gr.Row() with gr.Column(scale=1, min_width=300) per research.md R2 to enable automatic wrapping on smaller screens. Returns: List of gr.Image components for chart display """ chart_components = [] # Row 1: P/E and P/B with gr.Row(): with gr.Column(scale=1, min_width=300): pe_chart = gr.Image( label="P/E Ratio", type="filepath", show_label=True, ) chart_components.append(pe_chart) with gr.Column(scale=1, min_width=300): pb_chart = gr.Image( label="P/B Ratio", type="filepath", show_label=True, ) chart_components.append(pb_chart) # Row 2: P/S and EV/EBITDA with gr.Row(): with gr.Column(scale=1, min_width=300): ps_chart = gr.Image( label="P/S Ratio", type="filepath", show_label=True, ) chart_components.append(ps_chart) with gr.Column(scale=1, min_width=300): ev_chart = gr.Image( label="EV/EBITDA", type="filepath", show_label=True, ) chart_components.append(ev_chart) # Row 3: Profit Margins and ROE with gr.Row(): with gr.Column(scale=1, min_width=300): margins_chart = gr.Image( label="Profit Margins", type="filepath", show_label=True, ) chart_components.append(margins_chart) with gr.Column(scale=1, min_width=300): roe_chart = gr.Image( label="Return on Equity", type="filepath", show_label=True, ) chart_components.append(roe_chart) # Row 4: Growth and FCF with gr.Row(): with gr.Column(scale=1, min_width=300): growth_chart = gr.Image( label="Revenue & Earnings Growth", type="filepath", show_label=True, ) chart_components.append(growth_chart) with gr.Column(scale=1, min_width=300): fcf_chart = gr.Image( label="Free Cash Flow", type="filepath", show_label=True, ) chart_components.append(fcf_chart) # Row 5: Debt-to-Equity (centered in single column) with gr.Row(): with gr.Column(scale=1, min_width=300): debt_chart = gr.Image( label="Debt-to-Equity", type="filepath", show_label=True, ) chart_components.append(debt_chart) # Empty column for balance with gr.Column(scale=1, min_width=300): pass logger.info(f"Created desktop grid with {len(chart_components)} chart slots") return chart_components def create_mobile_grid(self) -> List[gr.Image]: """Create stacked mobile layout (1 column, 8+ rows). Returns: List of gr.Image components for chart display """ chart_components = [] with gr.Accordion("📊 Valuation Dashboard", open=True): # All charts stacked vertically pe_chart = gr.Image(label="P/E Ratio", type="filepath") chart_components.append(pe_chart) pb_chart = gr.Image(label="P/B Ratio", type="filepath") chart_components.append(pb_chart) ps_chart = gr.Image(label="P/S Ratio", type="filepath") chart_components.append(ps_chart) ev_chart = gr.Image(label="EV/EBITDA", type="filepath") chart_components.append(ev_chart) margins_chart = gr.Image(label="Profit Margins", type="filepath") chart_components.append(margins_chart) roe_chart = gr.Image(label="Return on Equity", type="filepath") chart_components.append(roe_chart) growth_chart = gr.Image(label="Revenue & Earnings Growth", type="filepath") chart_components.append(growth_chart) fcf_chart = gr.Image(label="Free Cash Flow", type="filepath") chart_components.append(fcf_chart) debt_chart = gr.Image(label="Debt-to-Equity", type="filepath") chart_components.append(debt_chart) return chart_components def extract_chart_paths(dashboard: ValuationDashboard) -> List[Optional[str]]: """Extract file paths from dashboard charts in display order. Args: dashboard: Generated ValuationDashboard object Returns: List of 9 file paths (or None for unavailable charts) """ from config.models import ChartType chart_order = [ ChartType.PE_RATIO, ChartType.PB_RATIO, ChartType.PS_RATIO, ChartType.EV_EBITDA, ChartType.PROFIT_MARGINS, ChartType.ROE, ChartType.REVENUE_EARNINGS_GROWTH, ChartType.FREE_CASH_FLOW, ChartType.DEBT_TO_EQUITY, ] paths = [] for chart_type in chart_order: chart = next((c for c in dashboard.charts if c.chart_type == chart_type), None) if chart: # Find the actual file path (this is a placeholder - need to track paths) # For now, return None as charts don't store file paths yet paths.append(None) else: paths.append(None) return paths