EU-Utility/plugins/import_export/plugin.py

334 lines
11 KiB
Python

# 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)