""" Controller für Profil-Export Orchestriert den Export-Prozess von Account-Profilen. """ import os import logging from typing import Optional, Dict, Any from pathlib import Path from PyQt5.QtWidgets import QFileDialog from database.db_manager import DatabaseManager from utils.profile_export_service import ProfileExportService from views.dialogs.profile_export_dialog import show_export_dialog from views.dialogs.export_success_dialog import show_export_success from views.widgets.modern_message_box import show_error, show_warning logger = logging.getLogger("profile_export_controller") class ProfileExportController: """Controller für Account-Profil-Export""" def __init__(self, db_manager: DatabaseManager): """ Initialisiert den Export-Controller. Args: db_manager: Datenbank-Manager """ self.db_manager = db_manager self.export_service = ProfileExportService() def export_account(self, parent_widget, account_id: int) -> bool: """ Startet den Export-Prozess für einen Account. Args: parent_widget: Parent-Widget für Dialoge account_id: ID des zu exportierenden Accounts Returns: True bei Erfolg, False bei Fehler oder Abbruch """ try: # 1. Account-Daten aus DB laden logger.info(f"Starte Export für Account-ID: {account_id}") account_data = self.db_manager.get_account(account_id) if not account_data: logger.error(f"Account nicht gefunden: ID {account_id}") show_error( parent_widget, "Account nicht gefunden", f"Account mit ID {account_id} konnte nicht gefunden werden." ) return False username = account_data.get("username", "Unbekannt") logger.info(f"Account geladen: {username}") # 2. Export-Dialog anzeigen accepted, formats, password_protect = show_export_dialog(parent_widget, username) if not accepted: logger.info("Export abgebrochen durch Nutzer") return False logger.info(f"Export-Optionen: Formate={formats}, Passwort={password_protect}") # 3. Speicherort wählen save_directory = self._select_save_location(parent_widget, account_data, formats, password_protect) if not save_directory: logger.info("Kein Speicherort ausgewählt - Export abgebrochen") return False logger.info(f"Speicherort: {save_directory}") # 4. Export durchführen files_dict, password = self.export_service.export_account( account_data, formats, password_protect ) if not files_dict: logger.error("Keine Dateien wurden generiert") show_error( parent_widget, "Export fehlgeschlagen", "Beim Export ist ein Fehler aufgetreten." ) return False # 5. Dateien speichern saved_files = [] for filename, content in files_dict.items(): file_path = os.path.join(save_directory, filename) try: with open(file_path, 'wb') as f: f.write(content) saved_files.append(filename) logger.info(f"Datei gespeichert: {filename}") except Exception as e: logger.error(f"Fehler beim Speichern von {filename}: {e}") show_error( parent_widget, "Fehler beim Speichern", f"Datei {filename} konnte nicht gespeichert werden:\n{str(e)}" ) return False # 6. Erfolgs-Dialog anzeigen show_export_success( parent_widget, saved_files, save_directory, password ) logger.info(f"Export erfolgreich: {len(saved_files)} Datei(en) gespeichert") return True except Exception as e: logger.error(f"Fehler beim Export: {e}", exc_info=True) # Benutzerfreundliche Fehlermeldungen error_message = str(e) if "reportlab" in error_message.lower(): error_message = ( "PDF-Export ist nicht verfügbar.\n\n" "Bitte installieren Sie die erforderlichen Bibliotheken:\n" "pip install reportlab svglib" ) elif "pyzipper" in error_message.lower(): error_message = ( "Passwortgeschützter Export ist nicht verfügbar.\n\n" "Bitte installieren Sie die erforderliche Bibliothek:\n" "pip install pyzipper" ) show_error( parent_widget, "Export fehlgeschlagen", f"Beim Export ist ein Fehler aufgetreten:\n\n{error_message}" ) return False def _select_save_location( self, parent_widget, account_data: Dict[str, Any], formats: list, password_protect: bool ) -> Optional[str]: """ Öffnet einen Datei-Dialog zur Auswahl des Speicherorts. Args: parent_widget: Parent-Widget account_data: Account-Daten formats: Liste der Export-Formate password_protect: Ob Passwortschutz aktiviert ist Returns: Ausgewähltes Verzeichnis oder None bei Abbruch """ # Standard-Dateiname generieren if password_protect: # Bei Passwortschutz wird eine ZIP erstellt default_filename = self.export_service.generate_filename( account_data, "zip" ) else: # Ohne Passwortschutz: Ersten Format als Beispiel nehmen first_format = formats[0] if formats else "csv" default_filename = self.export_service.generate_filename( account_data, first_format ) # Standard-Speicherort: Benutzer-Downloads-Ordner default_directory = str(Path.home() / "Downloads") if password_protect: # Für ZIP: File-Dialog file_path, _ = QFileDialog.getSaveFileName( parent_widget, "Profil exportieren", os.path.join(default_directory, default_filename), "ZIP-Archiv (*.zip)" ) if not file_path: return None # Verzeichnis aus Dateipfad extrahieren return os.path.dirname(file_path) else: # Für mehrere Dateien: Verzeichnis-Dialog directory = QFileDialog.getExistingDirectory( parent_widget, "Speicherort für Export auswählen", default_directory ) return directory if directory else None def export_multiple_accounts(self, parent_widget, account_ids: list) -> bool: """ Exportiert mehrere Accounts gleichzeitig. HINWEIS: Diese Funktion ist vorbereitet für zukünftige Erweiterung, wird aber gemäß ROADMAP.md vorerst NICHT implementiert (YAGNI). Args: parent_widget: Parent-Widget account_ids: Liste von Account-IDs Returns: True bei Erfolg, False bei Fehler """ show_warning( parent_widget, "Nicht verfügbar", "Mehrfach-Export ist derzeit nicht verfügbar.\n" "Bitte exportieren Sie Accounts einzeln." ) return False