Treat finished PCB review as complete, not a raw SSE error.
The status watcher synthesized pcb_error with pcb_status=complete (terminal) when the worker finished before pcb_complete arrived. Map that hatch to pcb_complete, publish the event before flipping status, and send /pcb to the layout report tree.
This commit is contained in:
@@ -38,6 +38,34 @@ _ANALYSIS_BUSY = frozenset({
|
||||
_PLACEMENT_ACTIVE = frozenset({"queued", "running"})
|
||||
|
||||
|
||||
def pcb_sse_terminal_from_status(
|
||||
pcb_status: str | None,
|
||||
pcb_state: dict | None,
|
||||
reason: str | None = None,
|
||||
) -> tuple[str, dict]:
|
||||
"""Map a terminal ``pcb_status`` to the SSE event the UI expects.
|
||||
|
||||
The events watcher used to emit ``pcb_error`` with
|
||||
``pcb_status=complete (terminal)`` when the worker finished but the
|
||||
log lagged — the progress page then showed that raw string.
|
||||
"""
|
||||
state = pcb_state if isinstance(pcb_state, dict) else {}
|
||||
pst = pcb_status or "draft"
|
||||
blob = f"{reason or ''} {state.get('error') or ''}"
|
||||
if pst == "complete" or "pcb_status=complete" in blob:
|
||||
return "pcb_complete", {
|
||||
"findings": int(state.get("findings") or 0),
|
||||
"domains": int(state.get("domains") or 0),
|
||||
"groups": int(state.get("groups") or 0),
|
||||
"synthetic": True,
|
||||
}
|
||||
if pst == "cancelled" or "pcb_status=cancelled" in blob:
|
||||
return "pcb_cancelled", {"synthetic": True}
|
||||
err = state.get("error") or reason or "pcb worker terminated without a terminal event"
|
||||
return "pcb_error", {"error": err, "synthetic": True}
|
||||
|
||||
|
||||
|
||||
def _load_constraints_map(
|
||||
extracted_dir: Path,
|
||||
storage: StorageBackend | None = None,
|
||||
@@ -262,6 +290,11 @@ async def run_pcb_pipeline(
|
||||
ws._upload_file("periscope-findings.json")
|
||||
_step(project_id, "write_report", "complete", f"{len(findings)} findings")
|
||||
|
||||
_publish(project_id, "pcb_complete", {
|
||||
"findings": len(findings),
|
||||
"domains": len(plan.domains),
|
||||
"groups": len(plan.groups),
|
||||
})
|
||||
proj_svc.update_project(
|
||||
storage, user_id, project_id,
|
||||
pcb_status="complete",
|
||||
@@ -273,11 +306,6 @@ async def run_pcb_pipeline(
|
||||
},
|
||||
pcb_cancel_requested=False,
|
||||
)
|
||||
_publish(project_id, "pcb_complete", {
|
||||
"findings": len(findings),
|
||||
"domains": len(plan.domains),
|
||||
"groups": len(plan.groups),
|
||||
})
|
||||
except Exception as e:
|
||||
logger.exception("pcb pipeline failed for %s", project_id)
|
||||
proj_svc.update_project(
|
||||
|
||||
Reference in New Issue
Block a user