Major refactoring of RFCP backend: - Modular propagation models (8 models) - SharedMemoryManager for terrain data - ProcessPoolExecutor parallel processing - WebSocket progress streaming - Building filtering pipeline (351k → 15k) - 82 unit tests Performance: Standard preset 38s → 5s (7.6x speedup) Known issue: Detailed preset timeout (fix in 3.1.0)
45 lines
1.2 KiB
Python
45 lines
1.2 KiB
Python
"""
|
|
Progress reporting for long-running calculations.
|
|
"""
|
|
|
|
import time
|
|
from typing import Optional, Callable, Awaitable
|
|
|
|
|
|
class ProgressTracker:
|
|
"""Track and report calculation progress."""
|
|
|
|
def __init__(
|
|
self,
|
|
total: int,
|
|
callback: Optional[Callable[[str, float, Optional[float]], Awaitable[None]]] = None,
|
|
phase: str = "calculating",
|
|
):
|
|
self.total = total
|
|
self.callback = callback
|
|
self.phase = phase
|
|
self.completed = 0
|
|
self.start_time = time.time()
|
|
|
|
@property
|
|
def progress(self) -> float:
|
|
if self.total == 0:
|
|
return 1.0
|
|
return self.completed / self.total
|
|
|
|
@property
|
|
def eta_seconds(self) -> Optional[float]:
|
|
if self.completed == 0:
|
|
return None
|
|
elapsed = time.time() - self.start_time
|
|
rate = self.completed / elapsed
|
|
remaining = self.total - self.completed
|
|
return remaining / rate if rate > 0 else None
|
|
|
|
def update(self, n: int = 1):
|
|
self.completed += n
|
|
|
|
async def report(self):
|
|
if self.callback:
|
|
await self.callback(self.phase, self.progress, self.eta_seconds)
|