mgbam commited on
Commit
ccdbd61
Β·
verified Β·
1 Parent(s): df65c2e

Update tools/plot_generator.py

Browse files
Files changed (1) hide show
  1. tools/plot_generator.py +47 -38
tools/plot_generator.py CHANGED
@@ -1,9 +1,17 @@
1
  # tools/plot_generator.py
 
 
 
 
2
  import os
3
  import tempfile
 
 
4
  import pandas as pd
5
  import plotly.graph_objects as go
6
- from typing import Tuple, Union
 
 
7
 
8
 
9
  def plot_metric_tool(
@@ -11,41 +19,42 @@ def plot_metric_tool(
11
  date_col: str,
12
  value_col: str,
13
  output_dir: str = "/tmp",
14
- title: str = None,
15
  line_width: int = 2,
16
- marker_size: int = 6
17
- ) -> Union[Tuple[go.Figure, str], str]:
18
  """
19
- Load CSV or Excel file, parse a time series metric, and return an interactive Plotly Figure
20
- plus a high-res PNG file path for static embedding.
21
 
22
- Returns:
23
- - (fig, img_path) on success
24
- - error string starting with '❌' on failure
 
25
  """
26
- # Load data
27
  ext = os.path.splitext(file_path)[1].lower()
28
  try:
29
- df = pd.read_excel(file_path) if ext in ('.xls', '.xlsx') else pd.read_csv(file_path)
 
 
 
 
30
  except Exception as exc:
31
  return f"❌ Failed to load file: {exc}"
32
 
33
- # Validate columns
34
  missing = [c for c in (date_col, value_col) if c not in df.columns]
35
  if missing:
36
  return f"❌ Missing column(s): {', '.join(missing)}"
37
 
38
- # Parse and clean
39
- try:
40
- df[date_col] = pd.to_datetime(df[date_col], errors='coerce')
41
- except Exception:
42
- return f"❌ Could not parse '{date_col}' as dates."
43
- df[value_col] = pd.to_numeric(df[value_col], errors='coerce')
44
  df = df.dropna(subset=[date_col, value_col])
45
  if df.empty:
46
- return f"❌ No valid data after cleaning '{date_col}'/'{value_col}'"
47
 
48
- # Aggregate duplicates and sort
49
  df = (
50
  df[[date_col, value_col]]
51
  .groupby(date_col, as_index=True)
@@ -53,35 +62,35 @@ def plot_metric_tool(
53
  .sort_index()
54
  )
55
 
56
- # Build figure
57
  fig = go.Figure(
58
- data=[
59
- go.Scatter(
60
- x=df.index,
61
- y=df[value_col],
62
- mode='lines+markers',
63
- line=dict(width=line_width),
64
- marker=dict(size=marker_size),
65
- name=value_col,
66
- )
67
- ]
68
  )
69
  fig.update_layout(
70
  title=title or f"{value_col} Trend",
71
  xaxis_title=date_col,
72
  yaxis_title=value_col,
73
- template='plotly_dark',
74
- hovermode='x unified'
75
  )
76
 
77
- # Save PNG
78
  os.makedirs(output_dir, exist_ok=True)
79
- tmp = tempfile.NamedTemporaryFile(suffix='.png', prefix='trend_', dir=output_dir, delete=False)
80
- img_path = tmp.name
 
 
81
  tmp.close()
82
  try:
83
- fig.write_image(img_path, scale=2)
84
  except Exception as exc:
85
  return f"❌ Failed saving image: {exc}"
86
 
87
- return fig, img_path
 
1
  # tools/plot_generator.py
2
+ # ------------------------------------------------------------
3
+ # Creates an interactive line‑and‑marker trend chart for any
4
+ # (date_col, value_col) pair and saves a hi‑res PNG copy.
5
+
6
  import os
7
  import tempfile
8
+ from typing import Tuple, Union
9
+
10
  import pandas as pd
11
  import plotly.graph_objects as go
12
+
13
+ # Alias for typing β€” every helper returns a go.Figure
14
+ Plot = go.Figure
15
 
16
 
17
  def plot_metric_tool(
 
19
  date_col: str,
20
  value_col: str,
21
  output_dir: str = "/tmp",
22
+ title: str | None = None,
23
  line_width: int = 2,
24
+ marker_size: int = 6,
25
+ ) -> Union[Tuple[Plot, str], str]:
26
  """
27
+ Build a (date, metric) trend chart.
 
28
 
29
+ Returns
30
+ -------
31
+ (fig, png_path) on success
32
+ error string on failure (string starts with '❌')
33
  """
34
+ # ── 1. Load CSV or Excel ──────────────────────────────────
35
  ext = os.path.splitext(file_path)[1].lower()
36
  try:
37
+ df = (
38
+ pd.read_excel(file_path)
39
+ if ext in (".xls", ".xlsx")
40
+ else pd.read_csv(file_path)
41
+ )
42
  except Exception as exc:
43
  return f"❌ Failed to load file: {exc}"
44
 
45
+ # ── 2. Validate columns ───────────────────────────────────
46
  missing = [c for c in (date_col, value_col) if c not in df.columns]
47
  if missing:
48
  return f"❌ Missing column(s): {', '.join(missing)}"
49
 
50
+ # ── 3. Parse & clean ──────────────────────────────────────
51
+ df[date_col] = pd.to_datetime(df[date_col], errors="coerce")
52
+ df[value_col] = pd.to_numeric(df[value_col], errors="coerce")
 
 
 
53
  df = df.dropna(subset=[date_col, value_col])
54
  if df.empty:
55
+ return f"❌ No valid data after cleaning '{date_col}' / '{value_col}'."
56
 
57
+ # Aggregate duplicate timestamps, sort by date
58
  df = (
59
  df[[date_col, value_col]]
60
  .groupby(date_col, as_index=True)
 
62
  .sort_index()
63
  )
64
 
65
+ # ── 4. Build Plotly figure ────────────────────────────────
66
  fig = go.Figure(
67
+ go.Scatter(
68
+ x=df.index,
69
+ y=df[value_col],
70
+ mode="lines+markers",
71
+ line=dict(width=line_width),
72
+ marker=dict(size=marker_size),
73
+ name=value_col,
74
+ )
 
 
75
  )
76
  fig.update_layout(
77
  title=title or f"{value_col} Trend",
78
  xaxis_title=date_col,
79
  yaxis_title=value_col,
80
+ template="plotly_dark",
81
+ hovermode="x unified",
82
  )
83
 
84
+ # ── 5. Save static PNG copy ───────────────────────────────
85
  os.makedirs(output_dir, exist_ok=True)
86
+ tmp = tempfile.NamedTemporaryFile(
87
+ prefix="trend_", suffix=".png", dir=output_dir, delete=False
88
+ )
89
+ png_path = tmp.name
90
  tmp.close()
91
  try:
92
+ fig.write_image(png_path, scale=2)
93
  except Exception as exc:
94
  return f"❌ Failed saving image: {exc}"
95
 
96
+ return fig, png_path