/ dashboard / components / charts.py
charts.py
  1  """
  2  Reusable Chart Components
  3  
  4  Provides consistent chart creation across the dashboard.
  5  """
  6  
  7  from typing import Optional, Dict, List
  8  import plotly.graph_objects as go
  9  import plotly.express as px
 10  import pandas as pd
 11  from dashboard import config
 12  
 13  
 14  def create_funnel_chart(data: pd.DataFrame, x_col: str, y_col: str, title: str = "Pipeline Funnel") -> go.Figure:
 15      """
 16      Create a funnel chart for pipeline visualization.
 17  
 18      Args:
 19          data: DataFrame with pipeline data
 20          x_col: Column name for counts
 21          y_col: Column name for stage names
 22          title: Chart title
 23  
 24      Returns:
 25          Plotly Figure
 26      """
 27      fig = go.Figure(go.Funnel(
 28          y=data[y_col],
 29          x=data[x_col],
 30          textinfo="value",
 31          marker={"color": list(config.STATUS_COLORS.values())[:len(data)]}
 32      ))
 33      fig.update_layout(
 34          title=title,
 35          height=400,
 36          margin=dict(l=20, r=20, t=40, b=20)
 37      )
 38      return fig
 39  
 40  
 41  def create_bar_chart(data: pd.DataFrame, x_col: str, y_col: str, title: str, color_col: Optional[str] = None) -> go.Figure:
 42      """
 43      Create a bar chart.
 44  
 45      Args:
 46          data: DataFrame
 47          x_col: X-axis column
 48          y_col: Y-axis column
 49          title: Chart title
 50          color_col: Optional column for color mapping
 51  
 52      Returns:
 53          Plotly Figure
 54      """
 55      if color_col and color_col in data.columns:
 56          fig = px.bar(data, x=x_col, y=y_col, title=title, color=color_col)
 57      else:
 58          fig = px.bar(data, x=x_col, y=y_col, title=title)
 59  
 60      fig.update_layout(height=400, margin=dict(l=20, r=20, t=40, b=20))
 61      return fig
 62  
 63  
 64  def create_pie_chart(data: pd.DataFrame, values_col: str, names_col: str, title: str, color_map: Optional[Dict] = None) -> go.Figure:
 65      """
 66      Create a pie chart.
 67  
 68      Args:
 69          data: DataFrame
 70          values_col: Column with values
 71          names_col: Column with names
 72          title: Chart title
 73          color_map: Optional color mapping dict
 74  
 75      Returns:
 76          Plotly Figure
 77      """
 78      fig = px.pie(
 79          data,
 80          values=values_col,
 81          names=names_col,
 82          title=title,
 83          color=names_col if color_map else None,
 84          color_discrete_map=color_map
 85      )
 86      fig.update_traces(textposition='inside', textinfo='percent+label')
 87      fig.update_layout(height=400, margin=dict(l=20, r=20, t=40, b=20))
 88      return fig
 89  
 90  
 91  def create_line_chart(data: pd.DataFrame, x_col: str, y_col: str, title: str, group_col: Optional[str] = None) -> go.Figure:
 92      """
 93      Create a line chart.
 94  
 95      Args:
 96          data: DataFrame
 97          x_col: X-axis column (usually date)
 98          y_col: Y-axis column
 99          title: Chart title
100          group_col: Optional column for grouping (creates multiple lines)
101  
102      Returns:
103          Plotly Figure
104      """
105      if group_col:
106          fig = px.line(data, x=x_col, y=y_col, color=group_col, title=title)
107      else:
108          fig = px.line(data, x=x_col, y=y_col, title=title)
109  
110      fig.update_layout(height=400, margin=dict(l=20, r=20, t=40, b=20))
111      return fig
112  
113  
114  def create_stacked_bar_chart(data: pd.DataFrame, x_col: str, y_cols: List[str], title: str, colors: Optional[List[str]] = None) -> go.Figure:
115      """
116      Create a stacked bar chart.
117  
118      Args:
119          data: DataFrame
120          x_col: X-axis column
121          y_cols: List of columns to stack
122          title: Chart title
123          colors: Optional list of colors for bars
124  
125      Returns:
126          Plotly Figure
127      """
128      fig = go.Figure()
129  
130      if not colors:
131          colors = px.colors.qualitative.Plotly
132  
133      for i, col in enumerate(y_cols):
134          fig.add_trace(go.Bar(
135              name=col.replace('_', ' ').title(),
136              x=data[x_col],
137              y=data[col],
138              marker_color=colors[i % len(colors)]
139          ))
140  
141      fig.update_layout(
142          barmode='stack',
143          title=title,
144          xaxis_title=x_col.replace('_', ' ').title(),
145          yaxis_title='Count',
146          height=400,
147          margin=dict(l=20, r=20, t=40, b=20)
148      )
149      return fig
150  
151  
152  def create_gauge_chart(value: float, title: str, max_value: float = 100, thresholds: Optional[Dict] = None) -> go.Figure:
153      """
154      Create a gauge chart (for coverage, etc.).
155  
156      Args:
157          value: Current value
158          title: Chart title
159          max_value: Maximum value for gauge
160          thresholds: Optional dict with 'good', 'warning', 'critical' values
161  
162      Returns:
163          Plotly Figure
164      """
165      if not thresholds:
166          thresholds = {'good': 80, 'warning': 70, 'critical': 0}
167  
168      fig = go.Figure(go.Indicator(
169          mode="gauge+number+delta",
170          value=value,
171          title={'text': title},
172          gauge={
173              'axis': {'range': [None, max_value]},
174              'bar': {'color': "darkblue"},
175              'steps': [
176                  {'range': [0, thresholds['warning']], 'color': "lightgray"},
177                  {'range': [thresholds['warning'], thresholds['good']], 'color': "lightyellow"},
178                  {'range': [thresholds['good'], max_value], 'color': "lightgreen"}
179              ],
180              'threshold': {
181                  'line': {'color': "red", 'width': 4},
182                  'thickness': 0.75,
183                  'value': thresholds['good']
184              }
185          }
186      ))
187      fig.update_layout(height=300, margin=dict(l=20, r=20, t=40, b=20))
188      return fig