SXXXXXXX_PyUCC/pyucc/gui/export_handler.py

265 lines
9.2 KiB
Python

"""
Export handler for PyUCC GUI.
Handles exporting results to CSV and other formats, including UCC-style reports.
"""
from tkinter import filedialog, messagebox
from pathlib import Path
from datetime import datetime
class ExportHandler:
"""Handles export functionality for results."""
def __init__(self, gui_app):
"""
Args:
gui_app: Reference to the main GUI application instance
"""
self.app = gui_app
def export_to_csv(self):
"""Export current results tree to CSV file."""
from ..utils.csv_exporter import export_rows_to_csv
path = filedialog.asksaveasfilename(
defaultextension=".csv",
filetypes=[("CSV files", "*.csv"), ("All files", "*")],
)
if not path:
return
headers = [c for c in self.app.results_tree["columns"]]
rows = (
self.app.results_tree.item(child, "values")
for child in self.app.results_tree.get_children()
)
try:
written = export_rows_to_csv(path, headers, rows)
messagebox.showinfo("Export", f"Exported {written} rows to {path}")
except Exception as e:
messagebox.showerror("Export Error", str(e))
def save_ucc_report(self):
"""Save UCC-style report based on current action and results."""
# Determine which action is currently active
current_action = getattr(self.app, '_current_action', None)
if not current_action:
messagebox.showwarning("Save Report", "No results available to save")
return
# Get default filename based on action
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
default_name = f"PyUcc_{current_action}_{timestamp}_outfile.txt"
path = filedialog.asksaveasfilename(
defaultextension=".txt",
initialfile=default_name,
filetypes=[("Text files", "*.txt"), ("All files", "*")],
)
if not path:
return
try:
if current_action == "countings":
self._save_counting_report(Path(path))
elif current_action == "differ":
self._save_differ_report(Path(path))
elif current_action == "metrics":
self._save_metrics_report(Path(path))
elif current_action == "scan":
self._save_scan_report(Path(path))
else:
messagebox.showwarning("Save Report", f"Report not implemented for {current_action}")
return
# Show success dialog with option to open the report
self._show_report_saved_dialog(path)
except Exception as e:
messagebox.showerror("Save Report Error", str(e))
def _show_report_saved_dialog(self, report_path: str):
"""Show dialog with option to open the saved report."""
import tkinter as tk
from tkinter import ttk
import subprocess
import os
# Create custom dialog
dialog = tk.Toplevel(self.app)
dialog.title("Report Saved")
dialog.transient(self.app)
dialog.grab_set()
# Center the dialog
dialog.geometry("500x180")
dialog.resizable(False, False)
# Icon
try:
dialog.iconbitmap(default=str(Path(__file__).parent.parent / "assets" / "icon.ico"))
except:
pass
# Main frame
main_frame = ttk.Frame(dialog, padding=20)
main_frame.pack(fill=tk.BOTH, expand=True)
# Success icon and message
ttk.Label(
main_frame,
text="✅ Report saved successfully!",
font=("Segoe UI", 11, "bold")
).pack(pady=(0, 10))
# File path (with text wrapping)
path_frame = ttk.Frame(main_frame)
path_frame.pack(fill=tk.X, pady=(0, 20))
ttk.Label(path_frame, text="Location:", font=("Segoe UI", 9)).pack(anchor=tk.W)
path_text = tk.Text(path_frame, height=2, wrap=tk.WORD, font=("Consolas", 9), bg="#f0f0f0", relief=tk.FLAT)
path_text.insert("1.0", report_path)
path_text.config(state=tk.DISABLED)
path_text.pack(fill=tk.X, pady=(5, 0))
# Buttons frame
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill=tk.X)
def open_report():
"""Open report file with default text editor."""
try:
if os.name == 'nt': # Windows
os.startfile(report_path)
elif os.name == 'posix': # macOS/Linux
subprocess.run(['xdg-open', report_path])
dialog.destroy()
except Exception as e:
messagebox.showerror("Open Error", f"Could not open file:\n{e}", parent=dialog)
def close_dialog():
dialog.destroy()
# Open Report button (primary)
open_btn = ttk.Button(
btn_frame,
text="📄 Open Report",
command=open_report,
width=20
)
open_btn.pack(side=tk.LEFT, padx=(0, 10))
# Close button
close_btn = ttk.Button(
btn_frame,
text="Close",
command=close_dialog,
width=15
)
close_btn.pack(side=tk.LEFT)
# Center dialog on parent
dialog.update_idletasks()
x = self.app.winfo_x() + (self.app.winfo_width() // 2) - (dialog.winfo_width() // 2)
y = self.app.winfo_y() + (self.app.winfo_height() // 2) - (dialog.winfo_height() // 2)
dialog.geometry(f"+{x}+{y}")
# Set focus to Open button
open_btn.focus_set()
# Bind Enter key to open report
dialog.bind('<Return>', lambda e: open_report())
dialog.bind('<Escape>', lambda e: close_dialog())
def _save_counting_report(self, output_path: Path):
"""Save counting results as UCC-style report."""
from ..utils.ucc_report_generator import UCCReportGenerator
# Collect results from cache
results_cache = getattr(self.app, '_results_cache', None)
if not results_cache:
raise ValueError("No counting results available")
# Convert cache to list if it's a dict
if isinstance(results_cache, dict):
results = list(results_cache.values())
elif isinstance(results_cache, list):
results = results_cache
else:
raise ValueError("Invalid results cache format")
# Get project root
project_root = getattr(self.app, '_last_scan_root', '')
# Generate command description
cmd_desc = f"PyUcc Counting Analysis - Project: {project_root}"
UCCReportGenerator.generate_counting_report(
results=results,
output_path=output_path,
command_description=cmd_desc,
base_path=project_root
)
def _save_differ_report(self, output_path: Path):
"""Save differ results as UCC-style report."""
from ..utils.ucc_report_generator import UCCReportGenerator
# Get differ results from _current_results
differ_result = getattr(self.app, '_current_results', None)
if not differ_result:
raise ValueError("No differ results available")
# Extract matched pairs
pairs = differ_result.get('pairs', [])
baseline_id = differ_result.get('baseline_id', 'unknown')
# Generate command description
cmd_desc = f"PyUcc Differential Analysis - Baseline: {baseline_id}"
UCCReportGenerator.generate_differ_report(
diff_results=pairs,
output_path=output_path,
baseline_id=baseline_id,
command_description=cmd_desc
)
def _save_metrics_report(self, output_path: Path):
"""Save metrics results as UCC-style report (Cyclomatic Complexity)."""
from ..utils.ucc_report_generator import UCCReportGenerator
# Collect results from cache
results_cache = getattr(self.app, '_results_cache', None)
if not results_cache:
raise ValueError("No metrics results available")
# Convert cache to list if it's a dict
if isinstance(results_cache, dict):
results = list(results_cache.values())
elif isinstance(results_cache, list):
results = results_cache
else:
raise ValueError("Invalid results cache format")
# Get project root
project_root = getattr(self.app, '_last_scan_root', '')
# Generate command description
cmd_desc = f"PyUcc Cyclomatic Complexity Analysis - Project: {project_root}"
UCCReportGenerator.generate_metrics_report(
results=results,
output_path=output_path,
command_description=cmd_desc,
base_path=project_root
)
def _save_scan_report(self, output_path: Path):
"""Save scan results as UCC-style report."""
# Scan is just a file list, can reuse counting format
self._save_counting_report(output_path)