Deduplicate campaign template rendering

This commit is contained in:
2026-07-21 13:14:32 +02:00
parent 3c305753d6
commit 641bead0d8
4 changed files with 164 additions and 84 deletions

View File

@@ -36,6 +36,10 @@ from govoplan_campaign.backend.campaign.models import (
)
from govoplan_campaign.backend.campaign.template_values import build_template_values
from govoplan_campaign.backend.services.zip_service import create_zip_archive
from govoplan_campaign.backend.template_rendering import (
find_unresolved_placeholders as _find_unresolved_placeholders,
render_template as _render_template,
)
from .models import (
CampaignBuildReport,
@@ -47,28 +51,6 @@ from .models import (
MessageValidationStatus,
)
_DOLLAR_FIELD_PATTERN = re.compile(r"(?<!\\)\$\{(.*?)(?<!\\)\}")
_BRACE_FIELD_PATTERN = re.compile(r"(?<!\\)\{\{\s*(.*?)\s*\}\}")
def _normalize_template_key(raw: str) -> str:
key = raw.strip()
if key.startswith("fields."):
key = key.removeprefix("fields.")
elif key.startswith("local."):
key = "local::" + key.removeprefix("local.")
elif key.startswith("global."):
key = "global::" + key.removeprefix("global.")
if key.startswith("local::") or key.startswith("global::"):
return key
if key.startswith("local:"):
return "local::" + key.removeprefix("local:")
if key.startswith("global:"):
return "global::" + key.removeprefix("global:")
return key
@dataclass(slots=True)
class BuiltMessage:
draft: MessageDraft
@@ -123,29 +105,6 @@ def _read_text(campaign_file: str | Path, raw_path: str | None, encoding: str =
return path.read_text(encoding=encoding)
def _render_template(template: str, values: dict[str, Any], *, keep_missing: bool = True) -> str:
def replace(match: re.Match[str]) -> str:
key = _normalize_template_key(match.group(1))
if key in values:
value = values[key]
return "" if value is None else str(value)
return match.group(0) if keep_missing else ""
rendered = _DOLLAR_FIELD_PATTERN.sub(replace, template)
rendered = _BRACE_FIELD_PATTERN.sub(replace, rendered)
return rendered.replace(r"\${", "${").replace(r"\}", "}")
def _find_unresolved_placeholders(text: str | None) -> set[str]:
if not text:
return set()
return {
_normalize_template_key(match.group(1))
for pattern in (_DOLLAR_FIELD_PATTERN, _BRACE_FIELD_PATTERN)
for match in pattern.finditer(text)
}
def _message_address(recipient: RecipientConfig | None) -> MessageAddress | None:
if recipient is None:
return None