"""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()}