168 lines
5.8 KiB
Python
168 lines
5.8 KiB
Python
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),
|
|
)
|