87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
"""Word 初稿渲染(python-docx)。
|
||
|
||
初稿结构:封面信息 → 目录骨架 → 六章(3/4/5 章自动成文,1/2/6 章素材要点 + [待补充] 占位)。
|
||
写入技能 output/ 目录(用户可取)。python-docx 为技能特有依赖(requirements.txt 声明)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from util.logging_config import get_skill_logger
|
||
from util.runtime_paths import get_output_dir
|
||
|
||
|
||
def _log():
|
||
try:
|
||
return get_skill_logger()
|
||
except RuntimeError:
|
||
return None
|
||
|
||
|
||
def _check_docx_available() -> None:
|
||
try:
|
||
import docx # noqa: F401
|
||
except ImportError as exc:
|
||
raise RuntimeError(
|
||
"python-docx 未安装(技能依赖安装异常);请重新安装技能,或先用 HTML 预览查看初稿内容。"
|
||
) from exc
|
||
|
||
|
||
def write_docx(project: dict[str, Any], sections: dict[str, list[str]], batch_no: str) -> str:
|
||
"""生成《结题报告(初稿)》docx,返回文件路径。"""
|
||
_check_docx_available()
|
||
import docx
|
||
from docx.shared import Pt
|
||
|
||
log = _log()
|
||
log.info("docx_render_start project_no=%s stage=docx", project.get("project_no"))
|
||
|
||
document = docx.Document()
|
||
|
||
title = document.add_heading("结题报告(初稿)", level=0)
|
||
title.alignment = 1
|
||
|
||
info_table = document.add_table(rows=0, cols=2)
|
||
info_table.style = "Table Grid"
|
||
for label, value in (
|
||
("项目名称", project.get("project_name")),
|
||
("项目编号", project.get("project_no")),
|
||
("项目负责人", project.get("pi_name")),
|
||
("所属学院", project.get("department")),
|
||
("项目类型", project.get("program")),
|
||
("执行周期", f"{project.get('start_date') or '—'} ~ {project.get('end_date') or '—'}"),
|
||
("生成批次", batch_no),
|
||
):
|
||
row = info_table.add_row().cells
|
||
row[0].text = str(label)
|
||
row[1].text = str(value if value not in (None, "") else "—")
|
||
|
||
document.add_heading("目录(骨架,终稿请按本单位模板调整)", level=1)
|
||
for section_title in sections:
|
||
document.add_paragraph(section_title, style="List Number")
|
||
|
||
for section_title, paragraphs in sections.items():
|
||
document.add_heading(section_title, level=1)
|
||
for paragraph in paragraphs:
|
||
text = str(paragraph)
|
||
p = document.add_paragraph()
|
||
run = p.add_run(text)
|
||
if text.startswith("[待补充"):
|
||
run.font.color.rgb = None # 占位文本以提示语区分,不依赖颜色
|
||
run.italic = True
|
||
|
||
notice = document.add_paragraph()
|
||
notice_run = notice.add_run(
|
||
"说明:本初稿由结题报告初稿助手按台账数据自动生成;数字章节(完成情况/决算/成果清单)请逐项核对,"
|
||
"叙事章节仅提供素材要点,须由项目负责人本人撰写终稿;严禁引入台账之外的成果或结论。"
|
||
)
|
||
notice_run.font.size = Pt(9)
|
||
|
||
project_no = str(project.get("project_no") or "未编号")
|
||
filename = f"结题报告初稿-{project_no}.docx"
|
||
path = get_output_dir() + "/" + filename
|
||
document.save(path)
|
||
log.info("docx_render_done project_no=%s path=%s stage=docx", project_no, path)
|
||
return path
|