接手這個 session 請直接從「待辦事項」開始,不需要重新分析。branch = feature/ssp-import-export-phase2。
這個 session 完整重寫了 SSP DOCX 匯出的模板與渲染器,對齊正式 CMMC SSP 文件格式。
get_docx() 回傳未渲染原件Root cause:DocxTemplate.get_docx() 回傳的是未 render 的原始 Document 物件。正確做法是 tpl.save(buf) → Document(buf) 重新開啟。
# 修正前(錯的)
tpl.render(context)
doc = tpl.get_docx() # ← 未渲染!
# 修正後
tpl.render(context)
rendered_buf = BytesIO()
tpl.save(rendered_buf)
rendered_buf.seek(0)
doc = Document(rendered_buf) # ← 渲染後的文件| 區段 | 變更 |
|---|---|
| 封面 | 標題居中(28pt bold)、系統名稱、下方 metadata 表(Version/Date/Prepared by) |
| 所有標題 / 欄位名稱 | 全改純英文,移除中英夾雜(如「Field 欄位」→「Field」、「系統資訊」→移除) |
| System Information | 動態 loop(只顯示有值的列),移除 "Field/Value" 深色表頭 |
| Responsible Organization | 動態 loop(只顯示有值的列:Name / Address / Phone / Email) |
| Parties | 移除 6 張個別角色表,改為一張 Participants 合併表(Name / Role / Email),party_type=organization 過濾掉只出現在 Org 區塊 |
| Assessment Objectives | 改為 bullet list(ao_name 直接列,不再有 statement_id 前綴重複問題) |
| 控制項 heading | "REQUIREMENTS 安全要求" → "Requirements" |
| 欄位比例 | kv 表格 label=5cm / value=11cm;各 loop 表格均設定欄寬 |
| 檔名 | 修正 ASCII-only(Python isalnum() 對中文回傳 True,改用 c.isascii() and c.isalnum()) |
| 檔案 | 類型 | 說明 |
|---|---|---|
scripts/generate_ssp_docx_template.py |
完整重寫 | 封面 + 所有 section,純英文,動態 loop 表格 |
app/oscal/templates/ssp/ssp_cmmc_template.docx |
重新產生 | 執行上方腳本產出 |
app/oscal/service/export/ssp_docx_generator.py |
大改 | render 流程修正、_build_org_rows、_build_participants、_build_system_info_rows、AO bullet list |
app/oscal/service/export/ssp_export_app_service.py |
小改 | 檔名 ASCII-only fix(2 處 safe_name 生成) |
app/oscal/service/export/ssp_export_model.py |
小改 | SspPartyExportItem.role_id、SspControlImplExportItem.control_requirement 新增欄位 |
app/oscal/service/export/ssp_mf_content_loader.py |
中改 | parties 填 role_id;controls 填 control_requirement |
app/oscal/service/export/ssp_version_content_loader.py |
大改 | _build_parties() 改從 oscal_responsible_parties 載入(而非舊的 metadata.parties),帶正確 role_id |
di_containers/oscal/oscal_containers.py |
小改 | ssp_version_content_loader 注入 responsible_party_domain_service + party_domain_service |
pyproject.toml |
dev-only | jedi-oscal path dep(不 commit) |
docs/changelog/2026-05-22-feat-ssp-docx-template-rebuild.md |
新增 | changelog |
SspDocxGenerator.generate()
↓
DocxTemplate(template_path) ← app/oscal/templates/ssp/ssp_cmmc_template.docx
tpl.render(context) ← 渲染靜態段落(封面~System Environment)
tpl.save(rendered_buf) ← 必須用 save(),get_docx() 是未渲染原件
Document(rendered_buf) ← 重新開啟
_append_control_sections(doc) ← python-docx 動態 append 每個控制項
_append_reference_docs(doc) ← python-docx 動態 append 附錄
doc.save(buf)
{
"system_name": str,
"version": str,
"export_date": str, # "YYYY-MM-DD"
"author": str,
"general_description": str,
"network_architecture": str,
"data_flow": str,
"system_info_rows": [{"label": str, "value": str}], # 只含有值的列
"org_rows": [{"label": str, "value": str}], # 只含有值的列
"participants": [{"name": str, "role_label": str, "email": str}], # person only
"inventory_items": [...],
"components": [...],
"leveraged_authorizations": [...],
}192.168.50.188:25432 / guidant_ai_devmodule_frame_uid = 3699fc01-3398-40af-be20-ef7913451a27(TEST Excel Import)blsadmin / Billows@123!BE 尚未重啟(本 session 沒有重啟 BE)
# 先找舊 process
lsof -ti:8000 | xargs kill -9
# 重啟
python main_socketio.pySmoke test(重啟後)
GET /oscal/ssp/export?module_frame_uid=3699fc01-3398-40af-be20-ef7913451a27&format=docxCommit 所有異動(smoke test 通過後)
git add app/oscal/service/export/ssp_docx_generator.py \
app/oscal/service/export/ssp_export_app_service.py \
app/oscal/service/export/ssp_export_model.py \
app/oscal/service/export/ssp_mf_content_loader.py \
app/oscal/service/export/ssp_version_content_loader.py \
app/oscal/templates/ssp/ssp_cmmc_template.docx \
di_containers/oscal/oscal_containers.py \
scripts/generate_ssp_docx_template.py \
docs/changelog/2026-05-22-feat-ssp-docx-template-rebuild.md \
docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-docx-template-rebuild.md
# pyproject.toml 的 path dep 變更不 commitssp_export_model.py 加欄位後更新兩個 loaderao_namenetwork_architecture 和 data_flow 欄位目前 MF / SSP version loader 都回傳空字串(尚無對應 DB 欄位)pyproject.toml 有 jedi-oscal path dep(dev 用),不要 commit