""" Heatmap Component Gradio component for displaying attack vulnerability heatmaps. """ import logging from typing import Any, List, Optional import gradio as gr import plotly.graph_objects as go from dashboard.schemas import HeatmapData from dashboard.utils import log_dashboard_event logger = logging.getLogger(__name__) def create_heatmap_chart() -> gr.Plot: """ Create heatmap chart component. Returns: Plot component """ plot = gr.Plot( label="Attack Vulnerability Heatmap", ) return plot def update_heatmap_chart( heatmap_data: Optional[HeatmapData], ) -> Any: """ Update heatmap chart with data. Args: heatmap_data: Heatmap data Returns: Plotly figure """ if heatmap_data is None or not heatmap_data.attack_types: # Return empty chart fig = go.Figure() fig.update_layout( title="No Data Available", xaxis=dict(title="Metrics"), yaxis=dict(title="Attack Types"), ) return fig log_dashboard_event("DASHBOARD_VIEW_HEATMAP", run_id=heatmap_data.run_id) # Create heatmap fig = go.Figure( data=go.Heatmap( z=heatmap_data.values, x=heatmap_data.metrics, y=heatmap_data.attack_types, colorscale="RdYlGn_r", # Red (high) to Green (low) - reversed zmin=0, zmax=1, colorbar=dict( title=dict(text="Metric Value", side="right"), ), hovertemplate=( "Attack: %{y}
" "Metric: %{x}
" "Value: %{z:.3f}" ), ) ) fig.update_layout( title="Attack Vulnerability Heatmap", xaxis=dict(title="Metrics"), yaxis=dict( title="Attack Types", autorange="reversed", # Top to bottom ), height=500, width=700, ) return fig def create_comparison_heatmap( heatmap_data_list: List[HeatmapData], model_names: List[str], ) -> Any: """ Create comparison heatmap across multiple runs. Args: heatmap_data_list: List of heatmap data model_names: List of model names Returns: Plotly figure """ if not heatmap_data_list: # Return empty chart fig = go.Figure() fig.update_layout(title="No Data Available") return fig # For comparison, we'll show the average vulnerability import numpy as np all_attack_types = set() for hd in heatmap_data_list: all_attack_types.update(hd.attack_types) attack_types = sorted(list(all_attack_types)) metrics = heatmap_data_list[0].metrics if heatmap_data_list else [] # Calculate average values avg_values = [] for attack_type in attack_types: row = [] for metric in metrics: values = [] for hd in heatmap_data_list: if attack_type in hd.attack_types and metric in hd.metrics: idx = hd.attack_types.index(attack_type) m_idx = hd.metrics.index(metric) values.append(hd.values[idx][m_idx]) if values: row.append(np.mean(values)) else: row.append(0.0) avg_values.append(row) fig = go.Figure( data=go.Heatmap( z=avg_values, x=metrics, y=attack_types, colorscale="RdYlGn_r", zmin=0, zmax=1, colorbar=dict( title=dict(text="Avg Value", side="right"), ), ) ) fig.update_layout( title="Average Attack Vulnerability Across Models", xaxis=dict(title="Metrics"), yaxis=dict( title="Attack Types", autorange="reversed", ), height=500, width=700, ) return fig class HeatmapChart: """ Heatmap chart component with state management. """ def __init__(self): """Initialize heatmap chart.""" self._current_data: Optional[HeatmapData] = None def set_data(self, data: HeatmapData) -> None: """ Set heatmap data. Args: data: Heatmap data """ self._current_data = data def get_data(self) -> Optional[HeatmapData]: """ Get current heatmap data. Returns: Current heatmap data or None """ return self._current_data def get_figure(self) -> Any: """ Get Plotly figure. Returns: Plotly figure """ return update_heatmap_chart(self._current_data) @staticmethod def create_empty() -> Any: """ Create empty heatmap chart. Returns: Empty Plotly figure """ fig = go.Figure() fig.update_layout( title="Select a run to view vulnerability heatmap", xaxis=dict(title="Metrics"), yaxis=dict(title="Attack Types"), height=400, width=600, ) return fig def get_heatmap_tooltip() -> str: """ Get heatmap tooltip explanation. Returns: Tooltip string """ return ( "Heatmap shows mean metric values for each attack type.\n\n" "Color Scale:\n" "• Red (1.0) = High vulnerability (bad)\n" "• Yellow (0.5) = Medium vulnerability\n" "• Green (0.0) = Low vulnerability (good)\n\n" "Formula: M_ij = mean(metric_j | attack_i)" )