79 lines
2.7 KiB
Python
79 lines
2.7 KiB
Python
"""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)(<h2\b[^>]*>.*?</h2>)", 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()}
|