63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .models import CampaignConfig, EntryConfig, FieldDefinition
|
|
|
|
|
|
def field_definitions_by_name(config: CampaignConfig) -> dict[str, FieldDefinition]:
|
|
"""Return campaign field definitions keyed by field id/name."""
|
|
|
|
return {field.name: field for field in config.fields}
|
|
|
|
|
|
def field_can_override(config: CampaignConfig, field_name: str) -> bool:
|
|
"""Return whether a recipient/entry value may override the global value.
|
|
|
|
Unknown fields remain overridable for backwards compatibility with older
|
|
campaigns and ad-hoc external mappings. Semantic validation reports unknown
|
|
field usage separately when a field list is configured.
|
|
"""
|
|
|
|
field = field_definitions_by_name(config).get(field_name)
|
|
if field is None:
|
|
return True
|
|
return field.can_override
|
|
|
|
|
|
def ignored_entry_field_overrides(config: CampaignConfig, entry: EntryConfig) -> list[str]:
|
|
"""Return recipient field keys that are ignored by the override policy."""
|
|
|
|
return sorted(name for name in entry.fields if not field_can_override(config, name))
|
|
|
|
|
|
def effective_entry_field_values(config: CampaignConfig, entry: EntryConfig) -> dict[str, Any]:
|
|
"""Return the local/effective field value map for one message entry.
|
|
|
|
Global values act as defaults for local template placeholders. Recipient
|
|
values replace those defaults only when the corresponding field allows
|
|
overrides. Fields that are unknown to the campaign definition keep the old
|
|
permissive behavior and remain usable as local values.
|
|
"""
|
|
|
|
values: dict[str, Any] = dict(config.global_values)
|
|
for key, value in entry.fields.items():
|
|
if field_can_override(config, key) and entry_field_has_override_value(value):
|
|
values[key] = value
|
|
return values
|
|
|
|
|
|
def entry_field_has_override_value(value: Any) -> bool:
|
|
"""Return whether an entry field should override a global default.
|
|
|
|
Empty recipient values are treated as "not set" so global_values remain the
|
|
effective local defaults. Numeric zero and boolean false are valid explicit
|
|
overrides.
|
|
"""
|
|
|
|
if value is None:
|
|
return False
|
|
if isinstance(value, str):
|
|
return value.strip() != ""
|
|
return True
|