Nicolas Cantu 088eab84b7 Platform docs, services, ia_dev submodule, smart_ide project config
- Add ia_dev submodule (projects/smart_ide on forge 4nk)
- Document APIs, orchestrator, gateway, local-office, rollout
- Add systemd/scripts layout; relocate setup scripts
- Remove obsolete nginx/enso-only docs from this repo scope
2026-04-03 16:07:58 +02:00

91 lines
3.1 KiB
Python

"""Apply document commands to a docx using python-docx. No fallback: raises on error."""
import io
import logging
from typing import Any
from docx import Document
logger = logging.getLogger(__name__)
def apply_replace_text(doc: Document, search: str, replace: str) -> None:
"""Replace first occurrence of search with replace in all paragraphs."""
if not search:
raise ValueError("replaceText: search must be non-empty")
for paragraph in doc.paragraphs:
if search in paragraph.text:
for run in paragraph.runs:
if search in run.text:
run.text = run.text.replace(search, replace, 1)
return
paragraph.text = paragraph.text.replace(search, replace, 1)
return
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
if search in paragraph.text:
for run in paragraph.runs:
if search in run.text:
run.text = run.text.replace(search, replace, 1)
return
paragraph.text = paragraph.text.replace(search, replace, 1)
return
logger.warning("replaceText: search string not found: %s", repr(search[:50]))
def apply_insert_paragraph(
doc: Document,
text: str,
position: str = "end",
) -> None:
"""Insert a paragraph. position: 'end' (default) or 'start'."""
new_para = doc.add_paragraph(text)
if position == "start" and len(doc.paragraphs) > 1:
# Move the new paragraph to the start (python-docx adds at end)
body = doc.element.body
new_el = new_para._element
body.remove(new_el)
body.insert(0, new_el)
elif position != "end" and position != "start":
raise ValueError("insertParagraph: position must be 'start' or 'end'")
def load_docx(content: bytes) -> Document:
"""Load docx from bytes."""
return Document(io.BytesIO(content))
def save_docx(doc: Document) -> bytes:
"""Save docx to bytes."""
buf = io.BytesIO()
doc.save(buf)
buf.seek(0)
return buf.read()
def apply_commands_docx(content: bytes, commands: list[dict[str, Any]]) -> bytes:
"""
Apply a list of commands to docx content. Returns new content.
Commands: { "type": "replaceText", "search": "...", "replace": "..." }
{ "type": "insertParagraph", "text": "...", "position": "end"|"start" }
"""
doc = load_docx(content)
for cmd in commands:
ctype = cmd.get("type")
if ctype == "replaceText":
apply_replace_text(
doc,
search=str(cmd.get("search", "")),
replace=str(cmd.get("replace", "")),
)
elif ctype == "insertParagraph":
apply_insert_paragraph(
doc,
text=str(cmd.get("text", "")),
position=str(cmd.get("position", "end")),
)
else:
raise ValueError(f"Unknown command type: {ctype}")
return save_docx(doc)