""" Export Window for TheChart Application Provides a GUI interface for exporting data and graphs to various formats. """ import tkinter as tk from collections.abc import Callable from pathlib import Path from tkinter import filedialog, messagebox, ttk from export_manager import ExportManager class ExportWindow: """Export window for data and graph export functionality.""" def __init__( self, parent: tk.Tk, export_manager: ExportManager, get_current_filtered_df: Callable[[], object] | None = None, ) -> None: self.parent = parent self.export_manager = export_manager self._get_current_filtered_df = get_current_filtered_df # Create the export window self.window = tk.Toplevel(parent) self.window.title("Export Data") self.window.geometry("500x450") # Made taller to ensure buttons are visible self.window.resizable(False, False) # Center the window self._center_window() # Make window modal self.window.transient(parent) self.window.grab_set() # Setup the UI self._setup_ui() def _center_window(self) -> None: """Center the export window on the parent window.""" self.window.update_idletasks() # Get window dimensions width = self.window.winfo_width() height = self.window.winfo_height() # Get parent window position and size parent_x = self.parent.winfo_rootx() parent_y = self.parent.winfo_rooty() parent_width = self.parent.winfo_width() parent_height = self.parent.winfo_height() # Calculate position to center on parent x = parent_x + (parent_width // 2) - (width // 2) y = parent_y + (parent_height // 2) - (height // 2) self.window.geometry(f"{width}x{height}+{x}+{y}") def _setup_ui(self) -> None: """Setup the export window UI.""" # Main frame main_frame = ttk.Frame(self.window, padding="15") main_frame.pack(fill=tk.BOTH, expand=True) # Title title_label = ttk.Label( main_frame, text="Export Data & Graphs", font=("Arial", 14, "bold") ) title_label.pack(pady=(0, 15)) # Create scrollable content area for the main content content_frame = ttk.Frame(main_frame) content_frame.pack(fill=tk.BOTH, expand=True) # Export info section self._create_info_section(content_frame) # Export options section self._create_options_section(content_frame) # Buttons section - always at the bottom self._create_buttons_section(main_frame) def _create_info_section(self, parent: ttk.Frame) -> None: """Create the data information section.""" info_frame = ttk.LabelFrame(parent, text="Data Summary", padding="10") info_frame.pack(fill=tk.X, pady=(0, 20)) # Get export info export_info = self.export_manager.get_export_info() # Display information if export_info["has_data"]: info_text = f"""Total Entries: {export_info["total_entries"]} Date Range: {export_info["date_range"]["start"]} to {export_info["date_range"]["end"]} Pathologies: {", ".join(export_info["pathologies"])} Medicines: {", ".join(export_info["medicines"])}""" else: info_text = "No data available for export." info_label = ttk.Label(info_frame, text=info_text, justify=tk.LEFT) info_label.pack(anchor=tk.W) def _create_options_section(self, parent: ttk.Frame) -> None: """Create the export options section.""" options_frame = ttk.LabelFrame(parent, text="Export Options", padding="10") options_frame.pack(fill=tk.X, pady=(0, 20)) # Include graph option (for PDF export) self.include_graph_var = tk.BooleanVar(value=True) graph_check = ttk.Checkbutton( options_frame, text="Include graph in PDF export", variable=self.include_graph_var, ) graph_check.pack(anchor=tk.W, pady=(0, 10)) # Export scope option self.scope_var = tk.StringVar(value="all") scope_frame = ttk.Frame(options_frame) scope_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(scope_frame, text="Scope:").pack(side=tk.LEFT) ttk.Radiobutton( scope_frame, text="All data", variable=self.scope_var, value="all" ).pack(side=tk.LEFT, padx=10) ttk.Radiobutton( scope_frame, text="Current (filtered) view", variable=self.scope_var, value="filtered", ).pack(side=tk.LEFT) # Format selection format_label = ttk.Label(options_frame, text="Export Format:") format_label.pack(anchor=tk.W) self.format_var = tk.StringVar(value="JSON") formats = ["JSON", "XML", "PDF"] for fmt in formats: radio = ttk.Radiobutton( options_frame, text=fmt, variable=self.format_var, value=fmt ) radio.pack(anchor=tk.W, padx=(20, 0)) def _create_buttons_section(self, parent: ttk.Frame) -> None: """Create the buttons section.""" # Add a separator for visual clarity separator = ttk.Separator(parent, orient="horizontal") separator.pack(fill=tk.X, pady=(10, 10)) button_frame = ttk.Frame(parent) button_frame.pack(fill=tk.X, pady=(0, 10)) # Export button with more prominent styling export_btn = ttk.Button( button_frame, text="Export...", command=self._handle_export ) export_btn.pack(side=tk.LEFT, padx=(10, 10), pady=5) # Cancel button cancel_btn = ttk.Button( button_frame, text="Cancel", command=self.window.destroy ) cancel_btn.pack(side=tk.RIGHT, padx=(10, 10), pady=5) def _handle_export(self) -> None: """Handle the export button click.""" # Check if we have data to export export_info = self.export_manager.get_export_info() if not export_info["has_data"]: messagebox.showwarning( "No Data", "There is no data available to export.", parent=self.window ) return # Get selected format selected_format = self.format_var.get() # Define file types for dialog file_types = { "JSON": [("JSON files", "*.json"), ("All files", "*.*")], "XML": [("XML files", "*.xml"), ("All files", "*.*")], "PDF": [("PDF files", "*.pdf"), ("All files", "*.*")], } # Default filename default_name = f"thechart_export.{selected_format.lower()}" # Show save dialog filename = filedialog.asksaveasfilename( parent=self.window, title=f"Export as {selected_format}", defaultextension=f".{selected_format.lower()}", filetypes=file_types[selected_format], initialfile=default_name, ) if not filename: return # Determine scope DataFrame (if requested and available) scoped_df = None if self.scope_var.get() == "filtered" and self._get_current_filtered_df: try: scoped_df = self._get_current_filtered_df() except Exception: scoped_df = None # Perform export based on selected format success = False try: if selected_format == "JSON": success = self.export_manager.export_data_to_json( filename, df=scoped_df ) elif selected_format == "XML": success = self.export_manager.export_data_to_xml(filename, df=scoped_df) elif selected_format == "PDF": include_graph = self.include_graph_var.get() success = self.export_manager.export_to_pdf( filename, include_graph=include_graph, df=scoped_df ) if success: messagebox.showinfo( "Export Successful", f"Data exported successfully to:\n{filename}", parent=self.window, ) # Ask if user wants to open the file location if messagebox.askyesno( "Open Location", "Would you like to open the file location?", parent=self.window, ): self._open_file_location(filename) self.window.destroy() else: messagebox.showerror( "Export Failed", f"Failed to export data as {selected_format}. " "Please check the logs for more details.", parent=self.window, ) except Exception as e: messagebox.showerror( "Export Error", f"An error occurred during export:\n{str(e)}", parent=self.window, ) def _open_file_location(self, filepath: str) -> None: """Open the file location in the system file manager.""" try: file_path = Path(filepath) directory = file_path.parent # Use system-specific command to open file manager import subprocess import sys if sys.platform == "win32": subprocess.run(["explorer", str(directory)], check=False) elif sys.platform == "darwin": subprocess.run(["open", str(directory)], check=False) else: # Linux and other Unix-like systems subprocess.run(["xdg-open", str(directory)], check=False) except Exception: # If opening file location fails, just ignore silently pass