""" 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('', lambda e: open_report()) dialog.bind('', 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)