109 lines
3.7 KiB
Python
109 lines
3.7 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from dataclasses import dataclass
|
|||
|
|
from datetime import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
from uuid import uuid4
|
|||
|
|
|
|||
|
|
from docx import Document
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass(frozen=True)
|
|||
|
|
class AnalysisReport:
|
|||
|
|
source_filename: str
|
|||
|
|
provider_name: str
|
|||
|
|
model_name: str
|
|||
|
|
matched_skills: list[str]
|
|||
|
|
summary: str
|
|||
|
|
findings: list[dict[str, str]]
|
|||
|
|
recommendations: list[str]
|
|||
|
|
raw_model_output: str
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _safe_stem(filename: str) -> str:
|
|||
|
|
stem = Path(filename).stem or "analysis"
|
|||
|
|
safe = "".join(ch if ch.isalnum() or ch in ("-", "_") else "_" for ch in stem)
|
|||
|
|
return safe[:60] or "analysis"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _report_base_path(report: AnalysisReport, output_dir: Path, suffix: str) -> Path:
|
|||
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|||
|
|
return output_dir / f"{_safe_stem(report.source_filename)}_{uuid4().hex[:8]}_analysis.{suffix}"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_markdown_report(report: AnalysisReport, output_dir: Path | str) -> Path:
|
|||
|
|
path = _report_base_path(report, Path(output_dir), "md")
|
|||
|
|
lines = [
|
|||
|
|
"# DOCX 规范分析报告",
|
|||
|
|
"",
|
|||
|
|
"## 基本信息",
|
|||
|
|
"",
|
|||
|
|
f"- 源文件:{report.source_filename}",
|
|||
|
|
f"- 分析时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
|||
|
|
f"- 模型供应商:{report.provider_name}",
|
|||
|
|
f"- 模型名称:{report.model_name}",
|
|||
|
|
f"- 命中技能:{', '.join(report.matched_skills) or '无'}",
|
|||
|
|
"",
|
|||
|
|
"## 总体结论",
|
|||
|
|
"",
|
|||
|
|
report.summary,
|
|||
|
|
"",
|
|||
|
|
"## 技能符合性矩阵",
|
|||
|
|
"",
|
|||
|
|
"| 状态 | 检查项 | 证据或说明 |",
|
|||
|
|
"| --- | --- | --- |",
|
|||
|
|
]
|
|||
|
|
for finding in report.findings:
|
|||
|
|
lines.append(
|
|||
|
|
f"| {finding.get('status', '')} | {finding.get('item', '')} | {finding.get('evidence', '').replace('|', '/')} |"
|
|||
|
|
)
|
|||
|
|
lines.extend(["", "## 修改建议", ""])
|
|||
|
|
for item in report.recommendations:
|
|||
|
|
lines.append(f"- {item}")
|
|||
|
|
lines.extend(["", "## 模型分析原文", "", report.raw_model_output])
|
|||
|
|
path.write_text("\n".join(lines), encoding="utf-8")
|
|||
|
|
return path
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_docx_report(report: AnalysisReport, output_dir: Path | str) -> Path:
|
|||
|
|
path = _report_base_path(report, Path(output_dir), "docx")
|
|||
|
|
document = Document()
|
|||
|
|
document.add_heading("DOCX 规范分析报告", level=0)
|
|||
|
|
document.add_heading("基本信息", level=1)
|
|||
|
|
for label, value in [
|
|||
|
|
("源文件", report.source_filename),
|
|||
|
|
("分析时间", datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
|
|||
|
|
("模型供应商", report.provider_name),
|
|||
|
|
("模型名称", report.model_name),
|
|||
|
|
("命中技能", ", ".join(report.matched_skills) or "无"),
|
|||
|
|
]:
|
|||
|
|
document.add_paragraph(f"{label}:{value}")
|
|||
|
|
|
|||
|
|
document.add_heading("总体结论", level=1)
|
|||
|
|
document.add_paragraph(report.summary)
|
|||
|
|
|
|||
|
|
document.add_heading("技能符合性矩阵", level=1)
|
|||
|
|
table = document.add_table(rows=1, cols=3)
|
|||
|
|
table.style = "Table Grid"
|
|||
|
|
headers = table.rows[0].cells
|
|||
|
|
headers[0].text = "状态"
|
|||
|
|
headers[1].text = "检查项"
|
|||
|
|
headers[2].text = "证据或说明"
|
|||
|
|
for finding in report.findings:
|
|||
|
|
row = table.add_row().cells
|
|||
|
|
row[0].text = finding.get("status", "")
|
|||
|
|
row[1].text = finding.get("item", "")
|
|||
|
|
row[2].text = finding.get("evidence", "")
|
|||
|
|
|
|||
|
|
document.add_heading("修改建议", level=1)
|
|||
|
|
for item in report.recommendations:
|
|||
|
|
document.add_paragraph(item, style="List Bullet")
|
|||
|
|
|
|||
|
|
document.add_heading("模型分析原文", level=1)
|
|||
|
|
for line in report.raw_model_output.splitlines() or ["无"]:
|
|||
|
|
document.add_paragraph(line)
|
|||
|
|
|
|||
|
|
document.add_paragraph("说明:模型分析结果需人工复核,不应直接作为正式审查结论。")
|
|||
|
|
document.save(path)
|
|||
|
|
return path
|