385 lines
14 KiB
Python
385 lines
14 KiB
Python
"""
|
|
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"
|
|
|
|
# Example: This plugin could depend on Game Reader Test for shared OCR utilities
|
|
# dependencies = {
|
|
# 'plugins': ['plugins.game_reader_test.plugin.GameReaderTestPlugin']
|
|
# }
|
|
|
|
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.gain_amount} pts", "#4ecdc4")
|
|
|
|
def _on_loot(self, event: LootEvent):
|
|
"""Handle loot events."""
|
|
self.event_counts['loot'] += 1
|
|
# LootEvent has items list, extract first item for display
|
|
if event.items:
|
|
item = event.items[0]
|
|
item_name = item.get('name', 'Unknown')
|
|
quantity = item.get('quantity', 1)
|
|
value = item.get('value', 0)
|
|
value_str = f" ({value:.2f} PED)" if value else ""
|
|
self._add_event("Loot", f"{item_name} x{quantity}{value_str} from {event.mob_name}", "#ff8c42")
|
|
else:
|
|
self._add_event("Loot", f"Loot from {event.mob_name}", "#ff8c42")
|
|
|
|
def _on_damage(self, event: DamageEvent):
|
|
"""Handle damage events."""
|
|
if event.is_outgoing:
|
|
self.event_counts['damage'] += 1
|
|
self._add_event("Damage", f"{event.damage_amount} damage ({event.damage_type})", "#ff6b6b")
|
|
else:
|
|
self.event_counts['damage_taken'] += 1
|
|
self._add_event("Damage Taken", f"{event.damage_amount} damage from {event.attacker_name}", "#ff4757")
|
|
|
|
def _on_global(self, event: GlobalEvent):
|
|
"""Handle global events."""
|
|
self.event_counts['global'] += 1
|
|
item_str = f" with {event.item_name}" if event.item_name else ""
|
|
self._add_event("Global", f"{event.player_name} got {event.achievement_type}{item_str} 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",
|
|
gain_amount=0.1234,
|
|
skill_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(),
|
|
mob_name="Atrox",
|
|
items=[{'name': 'Shrapnel', 'quantity': 100, 'value': 1.0}],
|
|
total_tt_value=1.0,
|
|
position=None
|
|
)
|
|
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(),
|
|
damage_amount=45,
|
|
damage_type="Impact",
|
|
is_critical=True,
|
|
target_name="Atrox",
|
|
attacker_name="Player",
|
|
is_outgoing=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:
|
|
# Call parent class method (BasePlugin.read_log)
|
|
log_lines = super().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()
|