67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""Utilities for rendering execution plan information in CLI output."""
|
||||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
|
|||
|
|
def fallback_plan_summary(execution_plan: Any) -> str:
|
|||
|
|
"""Build a short plan summary from summary and summary_by_reason.
|
|||
|
|
|
|||
|
|
This is used when execution_plan.human_summary is missing or empty.
|
|||
|
|
"""
|
|||
|
|
summary = getattr(execution_plan, "summary", {}) or {}
|
|||
|
|
summary_by_reason = getattr(execution_plan, "summary_by_reason", {}) or {}
|
|||
|
|
operations = getattr(execution_plan, "operations", []) or []
|
|||
|
|
|
|||
|
|
total = summary.get("total", len(operations))
|
|||
|
|
parts = [
|
|||
|
|
f"计划操作统计:共 {total} 条(move {summary.get('move', 0)},rename {summary.get('rename', 0)},"
|
|||
|
|
f"quarantine {summary.get('quarantine', 0)},no-op {summary.get('no-op', 0)})"
|
|||
|
|
]
|
|||
|
|
if summary_by_reason:
|
|||
|
|
parts.append(
|
|||
|
|
"原因分布:" + ";".join(f"{reason}: {count}" for reason, count in list(summary_by_reason.items())[:8])
|
|||
|
|
)
|
|||
|
|
return "\n".join(parts)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def preferred_plan_summary(execution_plan: Any) -> str:
|
|||
|
|
"""Return human_summary if available, otherwise fallback summary."""
|
|||
|
|
human_summary = getattr(execution_plan, "human_summary", "")
|
|||
|
|
if isinstance(human_summary, str) and human_summary.strip():
|
|||
|
|
return human_summary
|
|||
|
|
return fallback_plan_summary(execution_plan)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def render_review_preview(
|
|||
|
|
rows: list[dict[str, str]],
|
|||
|
|
preview_limit: int = 10,
|
|||
|
|
show_all: bool = False,
|
|||
|
|
) -> tuple[list[str], int]:
|
|||
|
|
"""Render high-risk review rows for console preview.
|
|||
|
|
|
|||
|
|
Returns a tuple of (lines, remaining_count).
|
|||
|
|
"""
|
|||
|
|
if preview_limit < 1:
|
|||
|
|
raise ValueError("preview_limit must be >= 1")
|
|||
|
|
|
|||
|
|
selected = rows if show_all else rows[:preview_limit]
|
|||
|
|
remaining = 0 if show_all else max(0, len(rows) - len(selected))
|
|||
|
|
|
|||
|
|
lines: list[str] = []
|
|||
|
|
for row in selected:
|
|||
|
|
idx = row.get("index", "?")
|
|||
|
|
operation_type = row.get("operation_type", "")
|
|||
|
|
flags = row.get("risk_flags", "") or "none"
|
|||
|
|
source_path = row.get("source_path", "")
|
|||
|
|
destination_path = row.get("destination_path", "")
|
|||
|
|
reason = row.get("reason", "")
|
|||
|
|
|
|||
|
|
lines.append(f" - [{idx}] {operation_type} | flags={flags}")
|
|||
|
|
lines.append(f" source: {source_path}")
|
|||
|
|
lines.append(f" destination: {destination_path or '(none)'}")
|
|||
|
|
lines.append(f" reason: {reason}")
|
|||
|
|
|
|||
|
|
return lines, remaining
|