feat(xmind): 思维导图增加「系统名称」根层级,根主题为运八网络货运平台
- export_XMind.py 新增最外层系统名称根主题:默认从项目画像「项目名称」解析,可用 --system-name 覆盖;原需求主题下沉为第二层 - 三个需求(安徽运八/物流公司管理/运单资料文件包下载)的 XMind 全部按新结构重新生成 - .gitignore 恢复远程规范忽略项(.idea/、.vscode/、output/node_modules/、~$*),并增补 output/excel_reports/、.pytest_cache/、xst/
This commit is contained in:
+37
-8
@@ -26,6 +26,22 @@ MANIFEST_NS = "urn:xmind:xmap:xmlns:manifest:1.0"
|
||||
|
||||
SEPARATOR_RE = re.compile(r"^\|\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?$")
|
||||
|
||||
# XMind 最外层根主题:系统名称(默认取项目画像,取不到则用默认值)
|
||||
DEFAULT_SYSTEM_NAME = "运八网络货运平台"
|
||||
PROJECT_PROFILE = REPO_ROOT / "knowledge_base" / "00_project" / "project_profile.md"
|
||||
|
||||
|
||||
def resolve_system_name() -> str:
|
||||
"""解析系统名称:优先取项目画像的「项目名称」,失败则回退默认值。"""
|
||||
try:
|
||||
text = PROJECT_PROFILE.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return DEFAULT_SYSTEM_NAME
|
||||
match = re.search(r"项目名称\*{0,2}[::]\s*([^((\n]+)", text)
|
||||
if not match:
|
||||
return DEFAULT_SYSTEM_NAME
|
||||
return match.group(1).strip() or DEFAULT_SYSTEM_NAME
|
||||
|
||||
|
||||
def _new_id():
|
||||
return str(uuid.uuid4())
|
||||
@@ -99,7 +115,8 @@ def normalize_text(value: str) -> str:
|
||||
return re.sub(r"<br\s*/?>", "\n", value or "").replace("\\n", "\n").strip()
|
||||
|
||||
|
||||
def build_xmind_content(headers: list[str], rows: list[list[str]], topic_name: str) -> bytes:
|
||||
def build_xmind_content(headers: list[str], rows: list[list[str]], topic_name: str,
|
||||
system_name: str = "") -> bytes:
|
||||
ts = _now_ms()
|
||||
nsmap = {None: XMAP_NS, "fo": FO_NS, "svg": SVG_NS, "xhtml": XHTML_NS, "xlink": XLINK_NS}
|
||||
|
||||
@@ -115,10 +132,16 @@ def build_xmind_content(headers: list[str], rows: list[list[str]], topic_name: s
|
||||
|
||||
root_topic = _el("topic", parent=sheet, id=_new_id(), timestamp=ts,
|
||||
**{"structure-class": "org.xmind.ui.logic.right"})
|
||||
_el("title", parent=root_topic).text = topic_name
|
||||
_el("title", parent=root_topic).text = system_name or DEFAULT_SYSTEM_NAME
|
||||
rc = _el("children", parent=root_topic)
|
||||
rt = _el("topics", parent=rc, type="attached")
|
||||
|
||||
# 第二层:需求名称
|
||||
need_topic = _el("topic", parent=rt, id=_new_id(), timestamp=ts)
|
||||
_el("title", parent=need_topic).text = topic_name
|
||||
nc = _el("children", parent=need_topic)
|
||||
nt = _el("topics", parent=nc, type="attached")
|
||||
|
||||
# 按模块路径分组
|
||||
grouped: dict[str, list[dict[str, str]]] = {}
|
||||
for row in rows:
|
||||
@@ -127,7 +150,7 @@ def build_xmind_content(headers: list[str], rows: list[list[str]], topic_name: s
|
||||
grouped.setdefault(module, []).append(case)
|
||||
|
||||
for module_name, cases in grouped.items():
|
||||
mtopic = _el("topic", parent=rt, id=_new_id(), timestamp=ts)
|
||||
mtopic = _el("topic", parent=nt, id=_new_id(), timestamp=ts)
|
||||
_el("title", parent=mtopic).text = module_name
|
||||
mc = _el("children", parent=mtopic)
|
||||
mtopics = _el("topics", parent=mc, type="attached")
|
||||
@@ -207,15 +230,18 @@ def build_meta() -> bytes:
|
||||
return etree.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=True)
|
||||
|
||||
|
||||
def export_markdown_to_xmind(source_path: Path, output_dir: Path, topic_name: str = "") -> Path:
|
||||
def export_markdown_to_xmind(source_path: Path, output_dir: Path, topic_name: str = "",
|
||||
system_name: str = "") -> Path:
|
||||
headers, rows = load_markdown_table(source_path)
|
||||
if not topic_name:
|
||||
topic_name = source_path.stem.replace("_测试用例", "").replace("_分析", "")
|
||||
if not system_name:
|
||||
system_name = resolve_system_name()
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_path = output_dir / f"{topic_name}.xmind"
|
||||
|
||||
content_xml = build_xmind_content(headers, rows, topic_name)
|
||||
content_xml = build_xmind_content(headers, rows, topic_name, system_name)
|
||||
manifest_xml = build_manifest()
|
||||
meta_xml = build_meta()
|
||||
|
||||
@@ -240,7 +266,9 @@ def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="将 Markdown 测试用例导出为 XMind 思维导图。")
|
||||
parser.add_argument("--source-file", help="测试用例 Markdown 文件路径")
|
||||
parser.add_argument("--base-name", help="按 BASE_NAME 查找 output/test_cases/{BASE_NAME}_测试用例.md")
|
||||
parser.add_argument("--topic-name", help="XMind 根主题名称(默认从文件名推断)", default="")
|
||||
parser.add_argument("--topic-name", help="需求主题名称(默认从文件名推断)", default="")
|
||||
parser.add_argument("--system-name", default="",
|
||||
help=f"XMind 最外层根主题:系统名称(默认取项目画像,回退 {DEFAULT_SYSTEM_NAME})")
|
||||
parser.add_argument("--input-dir", default=str(DEFAULT_INPUT_DIR), help="Markdown 所在目录")
|
||||
parser.add_argument("--output-dir", default=str(DEFAULT_OUTPUT_DIR), help="XMind 输出目录")
|
||||
args = parser.parse_args()
|
||||
@@ -259,11 +287,12 @@ def main() -> None:
|
||||
if not source_path.exists():
|
||||
raise FileNotFoundError(f"测试用例文件不存在:{source_path}")
|
||||
|
||||
output_path = export_markdown_to_xmind(source_path, output_dir, args.topic_name)
|
||||
topic_name = args.topic_name or source_path.stem.replace("_测试用例", "").replace("_分析", "")
|
||||
output_path = export_markdown_to_xmind(source_path, output_dir, args.topic_name, args.system_name)
|
||||
headers, rows = load_markdown_table(source_path)
|
||||
print(f"来源文件: {source_path}")
|
||||
print(f"用例数量: {len(rows)}")
|
||||
print(f"根主题: {args.topic_name or source_path.stem}")
|
||||
print(f"根主题: {args.system_name or resolve_system_name()} → {topic_name}")
|
||||
print(f"XMind 已生成: {output_path}")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user