# Description: Universal Import/Export tool for EU-Utility # Author: LemonNexus # Version: 1.0.0 """ Universal Import/Export tool for EU-Utility. Export and import all plugin data for backup and migration. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFileDialog, QProgressBar, QListWidget, QListWidgetItem, QMessageBox, QComboBox ) from PyQt6.QtCore import Qt, QThread, pyqtSignal import json import zipfile import os from datetime import datetime from plugins.base_plugin import BasePlugin class ExportImportWorker(QThread): """Background worker for export/import operations.""" progress = pyqtSignal(int) status = pyqtSignal(str) finished_signal = pyqtSignal(bool, str) def __init__(self, operation, data_store, path, selected_plugins=None): super().__init__() self.operation = operation self.data_store = data_store self.path = path self.selected_plugins = selected_plugins or [] def run(self): try: if self.operation == "export": self._do_export() else: self._do_import() except Exception as e: self.finished_signal.emit(False, str(e)) def _do_export(self): """Export data to zip file.""" self.status.emit("Gathering plugin data...") # Get all plugin data all_data = {} plugin_keys = self.data_store.get_all_keys("*") total = len(plugin_keys) for i, key in enumerate(plugin_keys): # Check if filtered plugin_name = key.split('.')[0] if '.' in key else key if self.selected_plugins and plugin_name not in self.selected_plugins: continue data = self.data_store.load(key, None) if data is not None: all_data[key] = data progress = int((i + 1) / total * 50) self.progress.emit(progress) self.status.emit("Creating export archive...") # Create zip file with zipfile.ZipFile(self.path, 'w', zipfile.ZIP_DEFLATED) as zf: # Add metadata metadata = { 'version': '2.0.0', 'exported_at': datetime.now().isoformat(), 'plugin_count': len(set(k.split('.')[0] for k in all_data.keys())), } zf.writestr('metadata.json', json.dumps(metadata, indent=2)) # Add data zf.writestr('data.json', json.dumps(all_data, indent=2)) self.progress.emit(75) self.progress.emit(100) self.finished_signal.emit(True, f"Exported {len(all_data)} data items") def _do_import(self): """Import data from zip file.""" self.status.emit("Reading archive...") with zipfile.ZipFile(self.path, 'r') as zf: # Verify metadata if 'metadata.json' not in zf.namelist(): raise ValueError("Invalid export file: missing metadata") metadata = json.loads(zf.read('metadata.json')) self.status.emit(f"Importing from version {metadata.get('version', 'unknown')}...") # Load data data = json.loads(zf.read('data.json')) self.progress.emit(25) # Import total = len(data) for i, (key, value) in enumerate(data.items()): # Check if filtered plugin_name = key.split('.')[0] if '.' in key else key if self.selected_plugins and plugin_name not in self.selected_plugins: continue self.data_store.save_raw(key, value) progress = 25 + int((i + 1) / total * 75) self.progress.emit(progress) self.finished_signal.emit(True, f"Imported {len(data)} data items") class ImportExportPlugin(BasePlugin): """Universal import/export tool for EU-Utility data.""" name = "Import/Export" version = "1.0.0" author = "LemonNexus" description = "Backup and restore all plugin data" icon = "archive" def initialize(self): """Initialize plugin.""" self.worker = None self.default_export_dir = os.path.expanduser("~/Documents/EU-Utility/Backups") os.makedirs(self.default_export_dir, exist_ok=True) def get_ui(self): """Create plugin UI.""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(15) # Title title = QLabel("Import / Export Data") title.setStyleSheet("font-size: 18px; font-weight: bold;") layout.addWidget(title) # Description desc = QLabel("Backup and restore all your EU-Utility data") desc.setStyleSheet("color: rgba(255,255,255,150);") layout.addWidget(desc) # Plugin selection layout.addWidget(QLabel("Select plugins to export/import:")) self.plugin_list = QListWidget() self.plugin_list.setSelectionMode(QListWidget.SelectionMode.MultiSelection) self._populate_plugin_list() layout.addWidget(self.plugin_list) # Select all/none buttons select_layout = QHBoxLayout() select_all_btn = QPushButton("Select All") select_all_btn.clicked.connect(lambda: self.plugin_list.selectAll()) select_layout.addWidget(select_all_btn) select_none_btn = QPushButton("Select None") select_none_btn.clicked.connect(lambda: self.plugin_list.clearSelection()) select_layout.addWidget(select_none_btn) layout.addLayout(select_layout) # Progress self.progress_bar = QProgressBar() self.progress_bar.setVisible(False) layout.addWidget(self.progress_bar) self.status_label = QLabel("") self.status_label.setStyleSheet("color: #4a9eff;") layout.addWidget(self.status_label) # Export/Import buttons btn_layout = QHBoxLayout() export_btn = QPushButton("Export to File...") export_btn.setStyleSheet(""" QPushButton { background-color: #4caf50; color: white; padding: 10px; font-weight: bold; } """) export_btn.clicked.connect(self._export) btn_layout.addWidget(export_btn) import_btn = QPushButton("Import from File...") import_btn.setStyleSheet(""" QPushButton { background-color: #ff8c42; color: white; padding: 10px; font-weight: bold; } """) import_btn.clicked.connect(self._import) btn_layout.addWidget(import_btn) layout.addLayout(btn_layout) # Info info = QLabel( "Exports include:\n" "- All plugin settings\n" "- Session history\n" "- Price alerts\n" "- Custom configurations\n\n" "Files are saved as .zip archives with JSON data." ) info.setStyleSheet("color: rgba(255,255,255,150); font-size: 11px;") layout.addWidget(info) layout.addStretch() return widget def _populate_plugin_list(self): """Populate the plugin list.""" # Get available plugins plugins = [ "loot_tracker", "skill_scanner", "price_alerts", "session_exporter", "mining_helper", "mission_tracker", "codex_tracker", "auction_tracker", "settings" ] for plugin in plugins: item = QListWidgetItem(plugin.replace('_', ' ').title()) item.setData(Qt.ItemDataRole.UserRole, plugin) self.plugin_list.addItem(item) def _get_selected_plugins(self): """Get list of selected plugin names.""" selected = [] for item in self.plugin_list.selectedItems(): selected.append(item.data(Qt.ItemDataRole.UserRole)) return selected def _export(self): """Export data to file.""" selected = self._get_selected_plugins() if not selected: QMessageBox.warning(self.get_ui(), "No Selection", "Please select at least one plugin to export.") return # Get save location timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") default_name = f"eu-utility-backup-{timestamp}.zip" path, _ = QFileDialog.getSaveFileName( self.get_ui(), "Export Data", os.path.join(self.default_export_dir, default_name), "Zip Files (*.zip)" ) if not path: return # Start export self._start_worker("export", path, selected) def _import(self): """Import data from file.""" selected = self._get_selected_plugins() if not selected: QMessageBox.warning(self.get_ui(), "No Selection", "Please select at least one plugin to import.") return # Confirm import reply = QMessageBox.question( self.get_ui(), "Confirm Import", "This will overwrite existing data for selected plugins. Continue?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply != QMessageBox.StandardButton.Yes: return # Get file location path, _ = QFileDialog.getOpenFileName( self.get_ui(), "Import Data", self.default_export_dir, "Zip Files (*.zip)" ) if not path: return # Start import self._start_worker("import", path, selected) def _start_worker(self, operation, path, selected): """Start background worker.""" # Show progress self.progress_bar.setVisible(True) self.progress_bar.setValue(0) # Get data store from API data_store = self.api.services.get('data_store') if self.api else None if not data_store: QMessageBox.critical(self.get_ui(), "Error", "Data store not available") return # Create and start worker self.worker = ExportImportWorker(operation, data_store, path, selected) self.worker.progress.connect(self.progress_bar.setValue) self.worker.status.connect(self.status_label.setText) self.worker.finished_signal.connect(self._on_worker_finished) self.worker.start() def _on_worker_finished(self, success, message): """Handle worker completion.""" self.progress_bar.setVisible(False) if success: self.status_label.setText(f"✓ {message}") self.status_label.setStyleSheet("color: #4caf50;") QMessageBox.information(self.get_ui(), "Success", message) else: self.status_label.setText(f"✗ {message}") self.status_label.setStyleSheet("color: #f44336;") QMessageBox.critical(self.get_ui(), "Error", message)