feat(regulatory): 增加风险归并与核查报告导出
This commit is contained in:
167
review_agent/regulatory_review/services/export.py
Normal file
167
review_agent/regulatory_review/services/export.py
Normal file
@@ -0,0 +1,167 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from openpyxl import Workbook
|
||||
|
||||
from review_agent.models import ExportedSummaryFile, RegulatoryIssue, RegulatoryReviewBatch
|
||||
|
||||
|
||||
SEVERITY_LABELS = {
|
||||
"blocking": "阻断项",
|
||||
"high": "高风险",
|
||||
"medium": "中风险",
|
||||
"low": "低风险",
|
||||
"info": "提示",
|
||||
}
|
||||
|
||||
|
||||
def export_review_results(batch: RegulatoryReviewBatch) -> list[ExportedSummaryFile]:
|
||||
root = Path(batch.work_dir) if batch.work_dir else Path(settings.MEDIA_ROOT) / "regulatory_review" / "work" / batch.batch_no
|
||||
export_dir = root / "exports"
|
||||
export_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
markdown = _create_export(
|
||||
batch,
|
||||
export_dir / f"{batch.batch_no}-regulatory-review.md",
|
||||
ExportedSummaryFile.ExportType.MARKDOWN,
|
||||
"markdown_report",
|
||||
build_markdown_report(batch),
|
||||
)
|
||||
excel = _create_excel_export(batch, export_dir / f"{batch.batch_no}-regulatory-issues.xlsx")
|
||||
result_json = _create_export(
|
||||
batch,
|
||||
export_dir / f"{batch.batch_no}-regulatory-result.json",
|
||||
ExportedSummaryFile.ExportType.JSON,
|
||||
"result_package",
|
||||
json.dumps(build_result_payload(batch), ensure_ascii=False, indent=2),
|
||||
)
|
||||
return [markdown, excel, result_json]
|
||||
|
||||
|
||||
def build_markdown_report(batch: RegulatoryReviewBatch) -> str:
|
||||
lines = [
|
||||
"# NMPA 注册资料法规核查报告",
|
||||
"",
|
||||
f"批次号:{batch.batch_no}",
|
||||
"",
|
||||
"## 风险汇总",
|
||||
"",
|
||||
"| 风险等级 | 数量 |",
|
||||
"| --- | --- |",
|
||||
]
|
||||
summary = batch.risk_summary or {}
|
||||
for severity, label in SEVERITY_LABELS.items():
|
||||
lines.append(f"| {label} | {summary.get(severity, 0)} |")
|
||||
lines.extend(["", "## 问题清单", "", "| 等级 | 问题 | 状态 | 建议 |", "| --- | --- | --- | --- |"])
|
||||
for issue in batch.issues.order_by("id"):
|
||||
lines.append(
|
||||
f"| {SEVERITY_LABELS.get(issue.severity, issue.severity)} | {issue.title} | {issue.status} | {issue.suggestion or '-'} |"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_result_payload(batch: RegulatoryReviewBatch) -> dict[str, object]:
|
||||
return {
|
||||
"batch_no": batch.batch_no,
|
||||
"source_summary_batch": batch.source_summary_batch.batch_no,
|
||||
"risk_summary": batch.risk_summary,
|
||||
"issues": [
|
||||
{
|
||||
"severity": issue.severity,
|
||||
"category": issue.category,
|
||||
"rule_code": issue.rule_code,
|
||||
"title": issue.title,
|
||||
"detail": issue.detail,
|
||||
"suggestion": issue.suggestion,
|
||||
"status": issue.status,
|
||||
"evidence": issue.evidence,
|
||||
"citations": issue.citations,
|
||||
}
|
||||
for issue in batch.issues.order_by("id")
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def build_assistant_summary(batch: RegulatoryReviewBatch, exports: list[ExportedSummaryFile]) -> str:
|
||||
export_by_type = {export.export_type: export for export in exports}
|
||||
lines = [
|
||||
"已完成 NMPA 注册资料法规核查。",
|
||||
"",
|
||||
"| 风险等级 | 数量 |",
|
||||
"| --- | --- |",
|
||||
]
|
||||
summary = batch.risk_summary or {}
|
||||
for severity, label in SEVERITY_LABELS.items():
|
||||
if summary.get(severity, 0):
|
||||
lines.append(f"| {label} | {summary[severity]} |")
|
||||
lines.extend(["", "| 等级 | 问题 | 状态 | 建议 |", "| --- | --- | --- | --- |"])
|
||||
for issue in batch.issues.order_by("id")[:8]:
|
||||
lines.append(
|
||||
f"| {SEVERITY_LABELS.get(issue.severity, issue.severity)} | {issue.title} | {issue.status} | {issue.suggestion or '-'} |"
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
_download_link("下载 Markdown 核查报告", export_by_type.get(ExportedSummaryFile.ExportType.MARKDOWN)),
|
||||
_download_link("下载 Excel 缺失清单", export_by_type.get(ExportedSummaryFile.ExportType.EXCEL)),
|
||||
_download_link("下载 JSON 结果包", export_by_type.get(ExportedSummaryFile.ExportType.JSON)),
|
||||
]
|
||||
)
|
||||
return "\n".join(line for line in lines if line is not None)
|
||||
|
||||
|
||||
def _download_link(label: str, exported: ExportedSummaryFile | None) -> str | None:
|
||||
if not exported:
|
||||
return None
|
||||
return f"[{label}](/api/review-agent/file-summary/exports/{exported.pk}/download/)"
|
||||
|
||||
|
||||
def _create_export(
|
||||
batch: RegulatoryReviewBatch,
|
||||
path: Path,
|
||||
export_type: str,
|
||||
category: str,
|
||||
content: str,
|
||||
) -> ExportedSummaryFile:
|
||||
path.write_text(content, encoding="utf-8")
|
||||
return ExportedSummaryFile.objects.create(
|
||||
batch=batch.source_summary_batch,
|
||||
workflow_type="regulatory_review",
|
||||
workflow_batch_id=batch.pk,
|
||||
export_category=category,
|
||||
export_type=export_type,
|
||||
file_name=path.name,
|
||||
storage_path=str(path),
|
||||
)
|
||||
|
||||
|
||||
def _create_excel_export(batch: RegulatoryReviewBatch, path: Path) -> ExportedSummaryFile:
|
||||
workbook = Workbook()
|
||||
sheet = workbook.active
|
||||
sheet.title = "法规问题清单"
|
||||
sheet.append(["等级", "类别", "规则", "问题", "状态", "建议", "法规依据"])
|
||||
for issue in batch.issues.order_by("id"):
|
||||
sheet.append(
|
||||
[
|
||||
SEVERITY_LABELS.get(issue.severity, issue.severity),
|
||||
issue.category,
|
||||
issue.rule_code,
|
||||
issue.title,
|
||||
issue.status,
|
||||
issue.suggestion,
|
||||
"; ".join(str(item.get("source", "")) for item in issue.citations),
|
||||
]
|
||||
)
|
||||
workbook.save(path)
|
||||
return ExportedSummaryFile.objects.create(
|
||||
batch=batch.source_summary_batch,
|
||||
workflow_type="regulatory_review",
|
||||
workflow_batch_id=batch.pk,
|
||||
export_category="issue_checklist",
|
||||
export_type=ExportedSummaryFile.ExportType.EXCEL,
|
||||
file_name=path.name,
|
||||
storage_path=str(path),
|
||||
)
|
||||
Reference in New Issue
Block a user