text = re.sub(VARIABLE_PATTERN, replace_fn, text) # Generated / configured values second pass gen_map = { "RandomInvoiceNumber": generated.invoice_number, "RandomTotal": f"${generated.amount:,.2f}" if generated.amount is not None else None, "RandomShippingCharge": f"${generated.shipping:,.2f}" if generated.shipping is not None else None, "RandomName": generated.random_name, "InvoiceDate": generated.invoice_date, "DueDate": generated.due_date, "CompanyPhone": generated.phone, "UnsubscribeLink": extra.get("unsubscribe_link", ""), } for var, val in gen_map.items(): if val not in (None, ""): text = text.replace(f"%{var}%", str(val)) used.append(var) return SubstitutionResult(text=text, unknown=sorted(set(unknown)), missing=sorted(set(missing)), used=sorted(set(used))) # ------------------------------------------------------------------ analysis def analyze(self, text: str, custom_fields: Optional[list] = None) -> dict: """Report which variables a template uses and any unknown ones (pre-send validation).""" found = set(VARIABLE_PATTERN.findall(text or "")) custom_fields = set(custom_fields or []) unknown = sorted(found - KNOWN_VARIABLES - custom_fields) return {"variables": sorted(found), "unknown": unknown} def analyze_template_variables(text: str, custom_fields: Optional[list] = None) -> dict: return VariableEngine().analyze(text, custom_fields)