""" 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) if not save_directory: logger.info("Kein Speicherort ausgewählt - Export abgebrochen") return False logger.info(f"Speicherort: {save_directory}") # 4. Export durchführen (ohne Passwortschutz) files_dict = self.export_service.export_account( account_data, formats, password_protect=False ) 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 ) 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" ) 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 ) -> Optional[str]: """ Öffnet einen Verzeichnis-Dialog zur Auswahl des Speicherorts. Args: parent_widget: Parent-Widget account_data: Account-Daten formats: Liste der Export-Formate Returns: Ausgewähltes Verzeichnis oder None bei Abbruch """ # Standard-Speicherort: Benutzer-Downloads-Ordner default_directory = str(Path.home() / "Downloads") # Verzeichnis-Dialog für mehrere Dateien 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. Args: parent_widget: Parent-Widget account_ids: Liste von Account-IDs Returns: True bei Erfolg, False bei Fehler """ try: if not account_ids: logger.warning("Keine Account-IDs zum Export übergeben") return False logger.info(f"Starte Batch-Export für {len(account_ids)} Accounts") # 1. Alle Account-Daten laden accounts_data = [] for account_id in account_ids: account_data = self.db_manager.get_account(account_id) if account_data: accounts_data.append(account_data) else: logger.warning(f"Account ID {account_id} nicht gefunden") if not accounts_data: show_error( parent_widget, "Keine Accounts gefunden", "Keiner der ausgewählten Accounts konnte geladen werden." ) return False logger.info(f"{len(accounts_data)} Accounts erfolgreich geladen") # 2. Export-Dialog anzeigen accepted, formats, _ = show_export_dialog(parent_widget, f"{len(accounts_data)} Accounts") if not accepted: logger.info("Batch-Export abgebrochen durch Nutzer") return False logger.info(f"Batch-Export-Optionen: Formate={formats}") # 3. Hauptordner wählen from datetime import datetime timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M") suggested_folder_name = f"AccountForge_Export_{timestamp}" save_directory = QFileDialog.getExistingDirectory( parent_widget, "Hauptordner für Batch-Export auswählen", str(Path.home() / "Downloads" / suggested_folder_name) ) if not save_directory: logger.info("Kein Speicherort ausgewählt - Export abgebrochen") return False # Hauptordner erstellen falls nicht vorhanden os.makedirs(save_directory, exist_ok=True) logger.info(f"Batch-Export-Ordner: {save_directory}") # 4. Für jeden Account exportieren exported_files = [] failed_accounts = [] for account_data in accounts_data: username = account_data.get("username", "unknown") platform = account_data.get("platform", "unknown").lower() try: # Plattform-Unterordner erstellen platform_dir = os.path.join(save_directory, platform) os.makedirs(platform_dir, exist_ok=True) # Export durchführen files_dict = self.export_service.export_account( account_data, formats, password_protect=False ) # Dateien mit vereinfachten Namen speichern (ohne Timestamp) for filename, content in files_dict.items(): # Vereinfachter Name: username.extension ext = filename.split('.')[-1] simple_filename = f"{username}.{ext}" file_path = os.path.join(platform_dir, simple_filename) with open(file_path, 'wb') as f: f.write(content) exported_files.append(f"{platform}/{simple_filename}") logger.info(f"Exportiert: {platform}/{simple_filename}") except Exception as e: logger.error(f"Fehler beim Export von {username}: {e}") failed_accounts.append(username) # 5. Summary-Datei erstellen summary_path = os.path.join(save_directory, "export_summary.txt") with open(summary_path, 'w', encoding='utf-8') as f: f.write(f"AccountForge Batch-Export\n") f.write(f"="*50 + "\n\n") f.write(f"Exportiert am: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n") f.write(f"Anzahl Accounts: {len(accounts_data)}\n") f.write(f"Erfolgreich: {len(accounts_data) - len(failed_accounts)}\n") if failed_accounts: f.write(f"Fehlgeschlagen: {len(failed_accounts)}\n") f.write(f"\nFormate: {', '.join(formats).upper()}\n") f.write(f"\n" + "="*50 + "\n\n") # Gruppiere nach Plattform platforms = {} for account_data in accounts_data: platform = account_data.get("platform", "unknown").lower() if platform not in platforms: platforms[platform] = [] platforms[platform].append(account_data.get("username", "")) for platform, usernames in sorted(platforms.items()): f.write(f"{platform.capitalize()}:\n") for username in usernames: if username in failed_accounts: f.write(f" ✗ {username} (FEHLER)\n") else: f.write(f" ✓ {username}\n") f.write(f"\n") exported_files.append("export_summary.txt") logger.info("Summary-Datei erstellt") # 6. Erfolgs-Dialog anzeigen show_export_success( parent_widget, exported_files, save_directory ) if failed_accounts: show_warning( parent_widget, "Teilweise erfolgreich", f"Export abgeschlossen, aber {len(failed_accounts)} Account(s) fehlgeschlagen:\n" + "\n".join(f"- {name}" for name in failed_accounts[:5]) + (f"\n... und {len(failed_accounts)-5} weitere" if len(failed_accounts) > 5 else "") ) logger.info(f"Batch-Export erfolgreich: {len(exported_files)} Datei(en)") return True except Exception as e: logger.error(f"Fehler beim Batch-Export: {e}", exc_info=True) show_error( parent_widget, "Batch-Export fehlgeschlagen", f"Beim Batch-Export ist ein Fehler aufgetreten:\n\n{str(e)}" ) return False