ispg-backend / models /visual_generator.py
urestrange's picture
Upload 162 files
cc2c355 verified
Raw
History Blame Contribute Delete
6.62 kB
# ==========================================================
# ISPG VISUAL GENERATOR (PATCHED STABLE VERSION)
# FILE: models/visual_generator.py
# ==========================================================
# PURPOSE:
# - Generate Flowchart PNG (Methodology)
# - Generate Results Table PNG
# - Generate Simple Bar Chart PNG (Metrics)
# - Extract Numeric Metrics from bullets
#
# IMPORTANT PATCH:
# - Use matplotlib Agg backend (NO tkinter GUI)
# - Prevent Flask threading crash: "main thread is not in main loop"
# ==========================================================
import os
import re
from typing import List, Dict, Any
# ==========================================================
# IMPORTANT FIX (MUST BE BEFORE pyplot import)
# ==========================================================
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
# ==========================================================
# HELPER: Extract numeric values from bullet strings
# ==========================================================
def extract_numeric_metrics(bullets: List[str]) -> Dict[str, float]:
"""
Example bullet:
"Accuracy: 92.5%"
"F1-score = 0.84"
"Execution time 12.4 seconds"
Returns dict:
{"Accuracy":92.5, "F1-score":0.84, "Execution time":12.4}
"""
metrics = {}
if not bullets or not isinstance(bullets, list):
return metrics
for b in bullets:
if not isinstance(b, str):
continue
text = b.strip()
if not text:
continue
# Match key + number (float/int)
match = re.search(r"([A-Za-z0-9\-\s]+)[:=]?\s*([0-9]+(\.[0-9]+)?)", text)
if match:
key = match.group(1).strip()
val = match.group(2).strip()
try:
val = float(val)
except:
continue
if key:
metrics[key] = val
return metrics
# ==========================================================
# FLOWCHART PNG GENERATOR
# ==========================================================
def generate_flowchart_png(steps: List[str], output_path: str) -> bool:
"""
Creates a simple vertical flowchart image using matplotlib.
"""
try:
if not steps or not isinstance(steps, list):
return False
steps = [s.strip() for s in steps if isinstance(s, str) and s.strip()]
if not steps:
return False
os.makedirs(os.path.dirname(output_path), exist_ok=True)
fig, ax = plt.subplots(figsize=(8, max(6, len(steps) * 1.2)))
ax.axis("off")
y = 1.0
spacing = 1.0 / (len(steps) + 1)
for i, step in enumerate(steps):
ax.text(
0.5,
y,
f"{i+1}. {step}",
ha="center",
va="center",
fontsize=12,
fontweight="bold",
bbox=dict(
boxstyle="round,pad=0.5",
fc="white",
ec="black"
)
)
# Draw arrow except last
if i < len(steps) - 1:
ax.annotate(
"",
xy=(0.5, y - spacing * 0.7),
xytext=(0.5, y - spacing * 0.2),
arrowprops=dict(arrowstyle="->", lw=2)
)
y -= spacing
plt.tight_layout()
plt.savefig(output_path, dpi=200, bbox_inches="tight")
plt.close(fig)
return os.path.exists(output_path)
except Exception as e:
print("❌ generate_flowchart_png failed:", str(e))
return False
# ==========================================================
# RESULTS TABLE PNG GENERATOR
# ==========================================================
def generate_results_table_png(results_tables: List[Dict[str, Any]], output_path: str) -> bool:
"""
Generate PNG table from first detected results table.
"""
try:
if not results_tables or not isinstance(results_tables, list):
return False
table = results_tables[0]
if not isinstance(table, dict):
return False
headers = table.get("headers", [])
rows = table.get("rows", [])
if not headers or not rows:
return False
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Adjust figure height dynamically
fig_height = max(4, min(10, len(rows) * 0.6))
fig, ax = plt.subplots(figsize=(12, fig_height))
ax.axis("off")
table_plot = ax.table(
cellText=rows,
colLabels=headers,
cellLoc="center",
loc="center"
)
table_plot.auto_set_font_size(False)
table_plot.set_fontsize(10)
table_plot.scale(1.2, 1.3)
plt.tight_layout()
plt.savefig(output_path, dpi=200, bbox_inches="tight")
plt.close(fig)
return os.path.exists(output_path)
except Exception as e:
print("❌ generate_results_table_png failed:", str(e))
return False
# ==========================================================
# RESULTS CHART PNG GENERATOR
# ==========================================================
def generate_results_chart_png(metrics_dict: Dict[str, float], output_path: str) -> bool:
"""
Generate bar chart from numeric metrics.
"""
try:
if not metrics_dict or not isinstance(metrics_dict, dict):
return False
labels = list(metrics_dict.keys())[:8]
values = []
for k in labels:
try:
values.append(float(metrics_dict[k]))
except:
values.append(0)
if not labels or not values:
return False
os.makedirs(os.path.dirname(output_path), exist_ok=True)
fig, ax = plt.subplots(figsize=(10, 5))
ax.bar(labels, values)
ax.set_title("Results Metrics")
ax.set_ylabel("Value")
ax.tick_params(axis="x", rotation=25)
plt.tight_layout()
plt.savefig(output_path, dpi=200, bbox_inches="tight")
plt.close(fig)
return os.path.exists(output_path)
except Exception as e:
print("❌ generate_results_chart_png failed:", str(e))
return False