121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""
|
|
Progress Bar Widget for PCAP parsing progress
|
|
"""
|
|
|
|
from textual.widget import Widget
|
|
from textual.reactive import reactive
|
|
from rich.console import RenderableType
|
|
from rich.progress import Progress, BarColumn, TextColumn, TaskProgressColumn, MofNCompleteColumn, TimeRemainingColumn
|
|
from rich.text import Text
|
|
import time
|
|
|
|
|
|
class ParsingProgressBar(Widget):
|
|
"""Progress bar widget for PCAP parsing with rich formatting"""
|
|
|
|
DEFAULT_CSS = """
|
|
ParsingProgressBar {
|
|
height: 3;
|
|
margin: 1;
|
|
padding: 1;
|
|
background: $surface;
|
|
border: solid $accent;
|
|
}
|
|
"""
|
|
|
|
# Reactive attributes
|
|
progress = reactive(0.0)
|
|
total_packets = reactive(0)
|
|
processed_packets = reactive(0)
|
|
packets_per_second = reactive(0.0)
|
|
estimated_time_remaining = reactive(0.0)
|
|
is_complete = reactive(False)
|
|
is_visible = reactive(False)
|
|
error_message = reactive("")
|
|
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.start_time = None
|
|
|
|
def render(self) -> RenderableType:
|
|
"""Render the progress bar"""
|
|
if not self.is_visible:
|
|
return Text("")
|
|
|
|
if self.error_message:
|
|
return Text(f"❌ Error: {self.error_message}", style="red")
|
|
|
|
if self.is_complete:
|
|
elapsed = time.time() - self.start_time if self.start_time else 0
|
|
return Text(
|
|
f"✅ Parsing complete! {self.processed_packets:,} packets processed in {elapsed:.1f}s",
|
|
style="green"
|
|
)
|
|
|
|
# Create rich progress bar
|
|
progress = Progress(
|
|
TextColumn("[bold blue]Parsing PCAP..."),
|
|
BarColumn(bar_width=40),
|
|
TaskProgressColumn(),
|
|
MofNCompleteColumn(),
|
|
TextColumn("•"),
|
|
TextColumn("{task.fields[rate]}"),
|
|
TextColumn("•"),
|
|
TimeRemainingColumn(),
|
|
expand=False
|
|
)
|
|
|
|
# Format rate display
|
|
if self.packets_per_second >= 1000:
|
|
rate_str = f"{self.packets_per_second/1000:.1f}K pkt/s"
|
|
else:
|
|
rate_str = f"{self.packets_per_second:.0f} pkt/s"
|
|
|
|
task = progress.add_task(
|
|
"parsing",
|
|
total=self.total_packets,
|
|
completed=self.processed_packets,
|
|
rate=rate_str
|
|
)
|
|
|
|
return progress
|
|
|
|
def start_parsing(self, total_packets: int):
|
|
"""Start showing progress for parsing"""
|
|
self.total_packets = total_packets
|
|
self.processed_packets = 0
|
|
self.progress = 0.0
|
|
self.is_complete = False
|
|
self.is_visible = True
|
|
self.error_message = ""
|
|
self.start_time = time.time()
|
|
self.refresh()
|
|
|
|
def update_progress(self, processed: int, total: int, pps: float, eta: float):
|
|
"""Update progress values"""
|
|
self.processed_packets = processed
|
|
self.total_packets = total
|
|
self.packets_per_second = pps
|
|
self.estimated_time_remaining = eta
|
|
self.progress = (processed / total * 100) if total > 0 else 0
|
|
self.refresh()
|
|
|
|
def complete_parsing(self):
|
|
"""Mark parsing as complete"""
|
|
self.is_complete = True
|
|
self.refresh()
|
|
# Hide after 3 seconds
|
|
self.set_timer(3.0, self.hide_progress)
|
|
|
|
def show_error(self, error: str):
|
|
"""Show error message"""
|
|
self.error_message = error
|
|
self.is_visible = True
|
|
self.refresh()
|
|
# Hide after 5 seconds
|
|
self.set_timer(5.0, self.hide_progress)
|
|
|
|
def hide_progress(self):
|
|
"""Hide the progress bar"""
|
|
self.is_visible = False
|
|
self.refresh() |