""" EU-Utility - Log Parser Test Plugin Debug and test tool for the Log Reader core service. Monitors log parsing in real-time with detailed output. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLabel, QPushButton, QComboBox, QCheckBox, QSpinBox, QGroupBox, QSplitter, QFrame, QTableWidget, QTableWidgetItem, QHeaderView ) from PyQt6.QtCore import Qt, QTimer from PyQt6.QtGui import QColor, QFont from plugins.base_plugin import BasePlugin from core.event_bus import SkillGainEvent, LootEvent, DamageEvent, GlobalEvent class LogParserTestPlugin(BasePlugin): """Test and debug tool for log parsing functionality.""" name = "Log Parser Test" version = "1.0.0" author = "EU-Utility" description = "Debug tool for testing log parsing and event detection" def __init__(self, overlay_window, config): super().__init__(overlay_window, config) self.event_counts = { 'skill_gain': 0, 'loot': 0, 'global': 0, 'damage': 0, 'damage_taken': 0, 'heal': 0, 'mission_complete': 0, 'tier_increase': 0, 'enhancer_break': 0, 'unknown': 0 } self.recent_events = [] self.max_events = 100 self.auto_scroll = True def initialize(self): """Initialize plugin and subscribe to events.""" # Subscribe to all event types for testing self.subscribe_typed(SkillGainEvent, self._on_skill_gain) self.subscribe_typed(LootEvent, self._on_loot) self.subscribe_typed(DamageEvent, self._on_damage) self.subscribe_typed(GlobalEvent, self._on_global) # Also subscribe to raw log events via API api = self._get_api() if api: api.subscribe('log_event', self._on_raw_log_event) self.log_info("Log Parser Test initialized - monitoring events...") def _get_api(self): """Get PluginAPI instance.""" try: from core.plugin_api import get_api return get_api() except: return None def _on_skill_gain(self, event: SkillGainEvent): """Handle skill gain events.""" self.event_counts['skill_gain'] += 1 self._add_event("Skill Gain", f"{event.skill_name}: +{event.points} pts", "#4ecdc4") def _on_loot(self, event: LootEvent): """Handle loot events.""" self.event_counts['loot'] += 1 value_str = f" ({event.value:.2f} PED)" if event.value else "" self._add_event("Loot", f"{event.item_name} x{event.quantity}{value_str}", "#ff8c42") def _on_damage(self, event: DamageEvent): """Handle damage events.""" if event.is_critical: self.event_counts['damage'] += 1 self._add_event("Damage", f"{event.amount} damage ({event.damage_type})", "#ff6b6b") else: self.event_counts['damage_taken'] += 1 self._add_event("Damage Taken", f"{event.amount} damage", "#ff4757") def _on_global(self, event: GlobalEvent): """Handle global events.""" self.event_counts['global'] += 1 self._add_event("Global", f"{event.player} found {event.item} worth {event.value} PED!", "#ffd93d") def _on_raw_log_event(self, event_data): """Handle raw log events.""" event_type = event_data.get('event_type', 'unknown') self.event_counts[event_type] = self.event_counts.get(event_type, 0) + 1 def _add_event(self, event_type: str, details: str, color: str): """Add event to recent events list.""" from datetime import datetime timestamp = datetime.now().strftime("%H:%M:%S") self.recent_events.insert(0, { 'time': timestamp, 'type': event_type, 'details': details, 'color': color }) # Trim to max if len(self.recent_events) > self.max_events: self.recent_events = self.recent_events[:self.max_events] # Update UI if available if hasattr(self, 'events_table'): self._update_events_table() def get_ui(self): """Create plugin UI.""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(12) # Header header = QLabel("📊 Log Parser Test & Debug Tool") header.setStyleSheet("font-size: 16px; font-weight: bold; color: #ff8c42;") layout.addWidget(header) # Status bar status_frame = QFrame() status_frame.setStyleSheet("background-color: #1a1f2e; border-radius: 6px; padding: 8px;") status_layout = QHBoxLayout(status_frame) self.status_label = QLabel("Status: Monitoring...") self.status_label.setStyleSheet("color: #4ecdc4;") status_layout.addWidget(self.status_label) status_layout.addStretch() self.log_path_label = QLabel() self._update_log_path() self.log_path_label.setStyleSheet("color: #666;") status_layout.addWidget(self.log_path_label) layout.addWidget(status_frame) # Event counters counters_group = QGroupBox("Event Counters") counters_layout = QHBoxLayout(counters_group) self.counter_labels = {} for event_type, color in [ ('skill_gain', '#4ecdc4'), ('loot', '#ff8c42'), ('global', '#ffd93d'), ('damage', '#ff6b6b'), ('damage_taken', '#ff4757') ]: frame = QFrame() frame.setStyleSheet(f"background-color: #1a1f2e; border-left: 3px solid {color}; padding: 8px;") frame_layout = QVBoxLayout(frame) name_label = QLabel(event_type.replace('_', ' ').title()) name_label.setStyleSheet("color: #888; font-size: 10px;") frame_layout.addWidget(name_label) count_label = QLabel("0") count_label.setStyleSheet(f"color: {color}; font-size: 20px; font-weight: bold;") frame_layout.addWidget(count_label) self.counter_labels[event_type] = count_label counters_layout.addWidget(frame) layout.addWidget(counters_group) # Recent events table events_group = QGroupBox("Recent Events (last 100)") events_layout = QVBoxLayout(events_group) self.events_table = QTableWidget() self.events_table.setColumnCount(3) self.events_table.setHorizontalHeaderLabels(["Time", "Type", "Details"]) self.events_table.horizontalHeader().setStretchLastSection(True) self.events_table.setStyleSheet(""" QTableWidget { background-color: #141f23; border: 1px solid #333; } QTableWidget::item { padding: 6px; } """) events_layout.addWidget(self.events_table) # Auto-scroll checkbox self.auto_scroll_cb = QCheckBox("Auto-scroll to new events") self.auto_scroll_cb.setChecked(True) events_layout.addWidget(self.auto_scroll_cb) layout.addWidget(events_group, 1) # Stretch factor 1 # Test controls controls_group = QGroupBox("Test Controls") controls_layout = QHBoxLayout(controls_group) # Simulate event buttons btn_layout = QHBoxLayout() test_skill_btn = QPushButton("Test: Skill Gain") test_skill_btn.clicked.connect(self._simulate_skill_gain) btn_layout.addWidget(test_skill_btn) test_loot_btn = QPushButton("Test: Loot") test_loot_btn.clicked.connect(self._simulate_loot) btn_layout.addWidget(test_loot_btn) test_damage_btn = QPushButton("Test: Damage") test_damage_btn.clicked.connect(self._simulate_damage) btn_layout.addWidget(test_damage_btn) clear_btn = QPushButton("Clear Events") clear_btn.clicked.connect(self._clear_events) btn_layout.addWidget(clear_btn) controls_layout.addLayout(btn_layout) controls_layout.addStretch() layout.addWidget(controls_group) # Raw log view raw_group = QGroupBox("Raw Log Lines (last 50)") raw_layout = QVBoxLayout(raw_group) self.raw_log_text = QTextEdit() self.raw_log_text.setReadOnly(True) self.raw_log_text.setStyleSheet(""" QTextEdit { background-color: #0d1117; color: #c9d1d9; font-family: Consolas, monospace; font-size: 11px; } """) raw_layout.addWidget(self.raw_log_text) layout.addWidget(raw_group) # Start update timer self.update_timer = QTimer() self.update_timer.timeout.connect(self._update_counters) self.update_timer.start(1000) # Update every second # Subscribe to raw log lines self.read_log(lines=50) # Pre-fill with recent log return widget def _update_log_path(self): """Update log path display.""" try: from core.log_reader import LogReader reader = LogReader() if reader.log_path: self.log_path_label.setText(f"Log: {reader.log_path}") else: self.log_path_label.setText("Log: Not found") except Exception as e: self.log_path_label.setText(f"Log: Error - {e}") def _update_counters(self): """Update counter displays.""" for event_type, label in self.counter_labels.items(): count = self.event_counts.get(event_type, 0) label.setText(str(count)) def _update_events_table(self): """Update events table with recent events.""" self.events_table.setRowCount(len(self.recent_events)) for i, event in enumerate(self.recent_events): # Time time_item = QTableWidgetItem(event['time']) time_item.setForeground(QColor("#888")) self.events_table.setItem(i, 0, time_item) # Type type_item = QTableWidgetItem(event['type']) type_item.setForeground(QColor(event['color'])) type_item.setFont(QFont("Segoe UI", weight=QFont.Weight.Bold)) self.events_table.setItem(i, 1, type_item) # Details details_item = QTableWidgetItem(event['details']) details_item.setForeground(QColor("#c9d1d9")) self.events_table.setItem(i, 2, details_item) # Auto-scroll if self.auto_scroll_cb.isChecked() and self.recent_events: self.events_table.scrollToTop() def _simulate_skill_gain(self): """Simulate a skill gain event for testing.""" from datetime import datetime event = SkillGainEvent( timestamp=datetime.now(), skill_name="Rifle", points=0.1234, new_value=1234.56 ) self._on_skill_gain(event) self.log_info("Simulated skill gain event") def _simulate_loot(self): """Simulate a loot event for testing.""" from datetime import datetime event = LootEvent( timestamp=datetime.now(), item_name="Shrapnel", quantity=100, value=1.0, mob_name="Atrox" ) self._on_loot(event) self.log_info("Simulated loot event") def _simulate_damage(self): """Simulate a damage event for testing.""" from datetime import datetime event = DamageEvent( timestamp=datetime.now(), amount=45, damage_type="Impact", is_critical=True ) self._on_damage(event) self.log_info("Simulated damage event") def _clear_events(self): """Clear all events.""" self.recent_events.clear() for key in self.event_counts: self.event_counts[key] = 0 self._update_events_table() self._update_counters() self.log_info("Cleared all events") def read_log(self, lines=50): """Read recent log lines.""" try: log_lines = self.read_log(lines=lines) if log_lines: self.raw_log_text.setPlainText('\n'.join(log_lines)) except Exception as e: self.raw_log_text.setPlainText(f"Error reading log: {e}") def on_show(self): """Called when plugin is shown.""" self._update_events_table() self._update_counters() def shutdown(self): """Clean up.""" if hasattr(self, 'update_timer'): self.update_timer.stop() super().shutdown()