import gradio as gr import pandas as pd import plotly.graph_objects as go class GeneralMetricsDisplay: def __init__(self): self.plots = [] @staticmethod def kpi_rate(percentage, title="KPI"): if percentage is None or not (0 <= percentage <= 100): fig = go.Figure() fig.update_layout( template='plotly_dark', width=320, height=150, margin=dict(l=10, r=10, t=10, b=10), annotations=[dict( text="No data", showarrow=False, font=dict(size=16, color="white"), x=0.5, y=0.5, xanchor="center", yanchor="middle" )] ) return fig fig = go.Figure(data=[go.Pie( values=[percentage, 100 - percentage], labels=['', ''], hole=0.6, marker_colors=['#2CFCFF', '#444444'], textinfo='none', hoverinfo='skip', sort=False, domain=dict(x=[0.4, 0.95], y=[0.15, 0.85]) )]) fig.update_layout( template='plotly_dark', annotations=[ dict( text=f"{percentage:.0f}%", x=0.675, y=0.5, font_size=20, showarrow=False, font=dict(color="white"), xanchor="center", yanchor="middle" ), dict( text=title, x=0.05, y=0.5, showarrow=False, font=dict(size=16, color="white"), xanchor="left", yanchor="middle" ) ], showlegend=False, margin=dict(l=10, r=10, t=10, b=10), width=320, height=150 ) return fig @staticmethod def kpi_value(value, title="Valeur"): width = 360 if value is None or (isinstance(value, str) and (value.strip() == "" or value.strip().isdigit() and len(value.strip()) > 8)): fig = go.Figure() fig.update_layout( template='plotly_dark', width=width, height=125, margin=dict(l=30, r=0, t=0, b=30), xaxis=dict(visible=False), yaxis=dict(visible=False), plot_bgcolor='#111111', paper_bgcolor='#111111', annotations=[dict( text="No data", showarrow=False, font=dict(size=16, color="white"), x=0.5, y=0.5, xanchor="center", yanchor="middle" )] ) return fig try: if isinstance(value, (int, float)): formatted = f"{int(value)}" if float(value).is_integer() else f"{float(value):.2f}" else: td = pd.to_timedelta(value) total_seconds = int(td.total_seconds()) hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) formatted = f"{hours}h {minutes}m {seconds}s" except (ValueError, TypeError): try: numeric_value = float(value) formatted = f"{int(numeric_value)}" if numeric_value.is_integer() else f"{numeric_value:.2f}" except (ValueError, TypeError): formatted = str(value) fig = go.Figure() fig.add_annotation( text=f"{formatted}", x=0.5, y=0.5, showarrow=False, font=dict(size=24, color="white"), xanchor="center", yanchor="middle" ) fig.add_annotation( text=title, x=0.5, y=1.8, showarrow=False, font=dict(size=16, color="lightgray"), xanchor="center", yanchor="middle" ) fig.update_layout( template='plotly_dark', width=width, height=150, margin=dict(l=40, r=0, t=0, b=40), xaxis=dict(visible=False), yaxis=dict(visible=False), plot_bgcolor='#111111', paper_bgcolor='#111111' ) return fig @staticmethod def get_max_part_id(df): if not df.empty and 'Part ID' in df.columns: try: numeric_ids = pd.to_numeric(df['Part ID'], errors='coerce') return int(numeric_ids.dropna().max()) except Exception: return None return None @staticmethod def pareto(issues_df, error_col='Error Type'): if issues_df is None or issues_df.empty: fig = go.Figure() fig.update_layout( template='plotly_dark', annotations=[dict( text="No Error", showarrow=False, font=dict(size=16, color="white") )] ) return fig issues_df['Downtime Start'] = pd.to_datetime(issues_df['Downtime Start'], errors='coerce') issues_df['Downtime End'] = pd.to_datetime(issues_df['Downtime End'], errors='coerce') issues_df['Downtime Duration'] = (issues_df['Downtime End'] - issues_df[ 'Downtime Start']).dt.total_seconds() / 60 issues_df = issues_df.dropna(subset=['Downtime Duration']) grouped = issues_df.groupby(error_col)['Downtime Duration'].sum().sort_values(ascending=False) if grouped.empty: fig = go.Figure() fig.update_layout( template='plotly_dark', annotations=[dict( text="No Error", showarrow=False, font=dict(size=16, color="white") )] ) return fig cumulative = grouped.cumsum() / grouped.sum() * 100 labels = grouped.index.tolist() durations = grouped.values fig = go.Figure() fig.add_trace( go.Bar( x=labels, y=durations, name='Downtime (min)', marker_color='#2CFCFF', yaxis='y1' ) ) fig.add_trace(go.Scatter( x=labels, y=cumulative, name='Cumulative %', yaxis='y2', mode='lines+markers', line=dict(color='orange', width=2), marker=dict(size=8) )) fig.update_layout( template='plotly_dark', title="Pareto of errors by downtime", xaxis=dict(title="Errors"), yaxis=dict( title='Downtime (minutes)', showgrid=False, side='left' ), yaxis2=dict( title='Cumulative percentage (%)', overlaying='y', side='right', range=[0, 110], showgrid=False, tickformat='%' ), legend=dict(x=0.7, y=1.1), margin=dict(l=70, r=70, t=50, b=50), ) return fig def general_block(self, all_tools_df, issues_df, status): header = f"Metrics Summary" html_content = f"""