SSP DOCX Format Handoff — 2026-05-22

§1

接手 Prompt

接手這個 session 請直接從「待辦事項」開始,不需要重新分析。branch = feature/ssp-import-export-phase2


§2

本 Session 完成內容

B6:SSP DOCX 格式升級(對齊 CMMC 樣板)

這個 session 完整重寫了 SSP DOCX 匯出的模板與渲染器,對齊正式 CMMC SSP 文件格式。

關鍵 bug fix:docxtpl get_docx() 回傳未渲染原件

Root causeDocxTemplate.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()

§3

異動檔案清單

檔案 類型 說明
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_idSspControlImplExportItem.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

§4

Context 重要資訊

Generator 架構

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)

Context Keys(傳給 docxtpl 的)

{
    "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": [...],
}

測試帳號 / 資料

  • Dev DB:192.168.50.188:25432 / guidant_ai_dev
  • MF 測試資料:module_frame_uid = 3699fc01-3398-40af-be20-ef7913451a27(TEST Excel Import)
  • 登入:blsadmin / Billows@123!

§5

待辦事項

立即要做(接手後第一件事)

  1. BE 尚未重啟(本 session 沒有重啟 BE)

    # 先找舊 process
    lsof -ti:8000 | xargs kill -9
    # 重啟
    python main_socketio.py
  2. Smoke test(重啟後)

    • MF export:GET /oscal/ssp/export?module_frame_uid=3699fc01-3398-40af-be20-ef7913451a27&format=docx
    • SSP version export:找一個有控制項的 SSP uid 測試
    • 確認:封面有系統名稱、System Information 只顯示有值的列、Participants 只有 person、控制項格式正確
  3. Commit 所有異動(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 變更不 commit

後續可能的調整

  • 使用者看到 DOCX 後可能還有格式調整需求(欄寬、字體、顏色)
  • 若需要新增更多 metadata 欄位(如 org contact info),在 ssp_export_model.py 加欄位後更新兩個 loader

§6

已知限制

  • AO 表格目前無翻譯(catalog 尚無 ZH 翻譯),只有英文 ao_name
  • MF 來源的 Responsible Organization 通常只有 Name,Address/Phone 留空
  • network_architecturedata_flow 欄位目前 MF / SSP version loader 都回傳空字串(尚無對應 DB 欄位)
  • pyproject.toml 有 jedi-oscal path dep(dev 用),不要 commit