"""Build docxtpl extra_context from ESPD state / request."""
from __future__ import annotations
import re
from typing import Any
from app.services.espd.html_util import html_to_plain
from app.services.espd.models import CONTENT_CONTEXT_KEYS, DocContent, DocumentType, EspdState
def resolve_doc_html_map(
state: EspdState,
doc_contents: dict[str, DocContent] | dict[str, Any] | None = None,
) -> dict[str, str]:
"""Return {doc_type: html} merging request override with state."""
out: dict[str, str] = {}
for dt in DocumentType:
if dt == DocumentType.SPEC:
continue
key = dt.value
html = ""
if doc_contents and key in doc_contents:
raw = doc_contents[key]
if isinstance(raw, DocContent):
html = raw.html or ""
elif isinstance(raw, dict):
html = str(raw.get("html") or "")
else:
html = str(raw or "")
if not html:
html = state.get_doc_html(key)
if html:
out[key] = html
return out
def _section_plain(html: str, *title_parts: str) -> str:
"""Extract plain text of first h2 whose title contains any of title_parts."""
if not html:
return ""
parts = re.split(r"(?i)(
]*>.*?
)", html)
i = 1
while i < len(parts):
heading = re.sub(r"<[^>]+>", "", parts[i])
body = parts[i + 1] if i + 1 < len(parts) else ""
lower = heading.lower()
if any(p.lower() in lower for p in title_parts):
return html_to_plain(body)
i += 2
return ""
def content_extra_context(doc_type: DocumentType, html_or_plain: str) -> dict[str, str]:
html = html_or_plain or ""
plain = html_to_plain(html) if "<" in html else html
extra: dict[str, str] = {"DOC_CONTENT": plain}
key = CONTENT_CONTEXT_KEYS.get(doc_type)
if key:
extra[key] = plain
# Sectional placeholders used by existing rp.docx / pmi.docx templates
if doc_type == DocumentType.RP:
purpose = _section_plain(html, "назначение")
conditions = _section_plain(html, "условия выполнения", "условия")
extra["PURPOSE"] = purpose or plain
extra["EXECUTION_CONDITIONS"] = conditions or ""
elif doc_type == DocumentType.PMI:
obj = _section_plain(html, "объект")
methods = _section_plain(html, "методы")
extra["TEST_OBJECT"] = obj or plain
extra["TEST_METHODS"] = methods or ""
return extra
def plain_contents_map(html_map: dict[str, str]) -> dict[str, str]:
return {k: html_to_plain(v) if v and "<" in v else (v or "") for k, v in html_map.items()}