from __future__ import annotations import io import json from typing import Any, Dict, Iterable, List def normalize_result_dicts(results: Iterable[Any]) -> List[Dict[str, Any]]: normalized: List[Dict[str, Any]] = [] for item in results: if hasattr(item, "to_dict"): normalized.append(item.to_dict()) elif isinstance(item, dict): normalized.append(item) else: normalized.append( { "requirement_uid": getattr(item, "requirement_uid", ""), "verdict": getattr(item, "verdict", ""), "coverage_score": getattr(item, "coverage_score", 0.0), "confidence": getattr(item, "confidence", 0.0), "matched_functions": getattr(item, "matched_functions", []), "covered_points": getattr(item, "covered_points", []), "missing_points": getattr(item, "missing_points", []), "conflict_points": getattr(item, "conflict_points", []), "call_chain_evidence": getattr(item, "call_chain_evidence", []), "suggestion": getattr(item, "suggestion", ""), "raw_judgment": getattr(item, "raw_judgment", {}), } ) return normalized def export_json(results: Iterable[Any]) -> bytes: return json.dumps( {"results": normalize_result_dicts(results)}, ensure_ascii=False, indent=2, ).encode("utf-8") def export_markdown(results: Iterable[Any]) -> str: rows = normalize_result_dicts(results) lines = [ "# 需求代码一致性比对报告", "", "| 需求 ID | 判定 | 覆盖分 | 置信度 | 匹配函数 | 缺失点 | 建议 |", "| --- | --- | ---: | ---: | ---: | ---: | --- |", ] for item in rows: lines.append( "| {uid} | {verdict} | {score:.2f} | {confidence:.2f} | {functions} | {missing} | {suggestion} |".format( uid=item.get("requirement_uid", ""), verdict=item.get("verdict", ""), score=float(item.get("coverage_score") or 0), confidence=float(item.get("confidence") or 0), functions=len(item.get("matched_functions") or []), missing=len(item.get("missing_points") or []), suggestion=str(item.get("suggestion") or "").replace("|", "/"), ) ) for item in rows: lines.extend( [ "", f"## {item.get('requirement_uid', '')} {item.get('requirement_title', '')}", "", f"- 判定: `{item.get('verdict', '')}`", f"- 覆盖分: {float(item.get('coverage_score') or 0):.2f}", f"- 置信度: {float(item.get('confidence') or 0):.2f}", f"- 建议: {item.get('suggestion') or '-'}", "", "### 匹配函数", ] ) for function in item.get("matched_functions") or []: lines.append( f"- `{function.get('name')}` {function.get('file')}:{function.get('start_line')} " f"(similarity={float(function.get('similarity') or 0):.2f})" ) lines.extend(["", "### 缺失点"]) for point in item.get("missing_points") or ["-"]: lines.append(f"- {point}") if item.get("conflict_points"): lines.extend(["", "### 冲突点"]) for point in item.get("conflict_points") or []: lines.append(f"- {point}") return "\n".join(lines) def export_excel(results: Iterable[Any]) -> bytes: try: from openpyxl import Workbook except ImportError as exc: raise RuntimeError("openpyxl is required to export Excel reports.") from exc rows = normalize_result_dicts(results) workbook = Workbook() sheet = workbook.active sheet.title = "Consistency" headers = [ "需求ID", "需求标题", "需求类型", "判定", "覆盖分", "置信度", "匹配函数数量", "主要文件", "缺失点数量", "建议", ] sheet.append(headers) for item in rows: functions = item.get("matched_functions") or [] sheet.append( [ item.get("requirement_uid", ""), item.get("requirement_title", ""), item.get("requirement_type", ""), item.get("verdict", ""), item.get("coverage_score", 0), item.get("confidence", 0), len(functions), functions[0].get("file", "") if functions else "", len(item.get("missing_points") or []), item.get("suggestion", ""), ] ) output = io.BytesIO() workbook.save(output) return output.getvalue()