import os import tempfile import pandas as pd import plotly.graph_objects as go def plot_metric_tool( file_path: str, date_col: str, value_col: str, output_dir: str = "/tmp", title: str = None, line_width: int = 2, marker_size: int = 6 ): """ Load CSV or Excel file, parse a time series metric, and return an interactive Plotly Figure. Also saves a high-resolution PNG to a temp directory for static embedding. Returns: fig (go.Figure) or error string starting with '❌'. """ # 0) Load data ext = os.path.splitext(file_path)[1].lower() try: if ext in ('.xls', '.xlsx'): df = pd.read_excel(file_path) else: df = pd.read_csv(file_path) except Exception as exc: return f"❌ Failed to load file: {exc}" # 1) Validate columns missing = [c for c in (date_col, value_col) if c not in df.columns] if missing: return f"❌ Missing column(s): {', '.join(missing)}" # 2) Parse date and ensure numeric values try: df[date_col] = pd.to_datetime(df[date_col], errors='coerce') except Exception: return f"❌ Could not parse '{date_col}' as dates." df[value_col] = pd.to_numeric(df[value_col], errors='coerce') df = df.dropna(subset=[date_col, value_col]) if df.empty: return f"❌ No valid data after cleaning '{date_col}'/'{value_col}'" # 3) Sort and aggregate duplicates df = ( df[[date_col, value_col]] .groupby(date_col, as_index=True) .mean() .sort_index() ) # 4) Create Plotly figure fig = go.Figure( data=[ go.Scatter( x=df.index, y=df[value_col], mode='lines+markers', line=dict(width=line_width), marker=dict(size=marker_size), name=value_col ) ] ) plot_title = title or f"{value_col} Trend" fig.update_layout( title=plot_title, xaxis_title=date_col, yaxis_title=value_col, template='plotly_dark', hovermode='x unified' ) # 5) Save static PNG os.makedirs(output_dir, exist_ok=True) tmpfile = tempfile.NamedTemporaryFile( suffix='.png', prefix='trend_', dir=output_dir, delete=False ) img_path = tmpfile.name tmpfile.close() try: fig.write_image(img_path, scale=2) except Exception as exc: return f"❌ Failed saving image: {exc}" # 6) Return figure and path for embedding return fig, img_path