trucking: add idempotent patcher for main-campaign coupon blocks (186/188 computed prices)

This commit is contained in:
justin 2026-06-20 17:43:46 -05:00
parent 579919197d
commit 2f0753f00e

View file

@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""Patch the two main trucking source campaigns (MCS-150 #186, Inactive USDOT
#188) coupon blocks to use the build step's on-the-fly computed prices
(coupon_price_full / coupon_price_deal) instead of the hardcoded "$79 $47" /
"$149 $89", which were only true at 40% off and would be false on the 20/30%
A/B arms.
Idempotent: re-running replaces the coupon if-block again with the same content.
Run inside the workers container on prod:
python3 scripts/patch_main_coupon_blocks.py [--dry-run]
"""
from __future__ import annotations
import argparse
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from scripts import build_trucking_campaigns as b # reuse lm_api + auth
def _deal_block(service_phrase: str, extra_expiry_line: str = "") -> str:
"""Coupon-aware offer block matching the deficiency _deal_box structure:
real was/now prices when priceable, percent-only otherwise."""
priced = (
'<p style="font-size:18px;font-weight:700;color:#9a3412;margin:0 0 4px">'
f'{service_phrase} for '
'<span style="text-decoration:line-through;color:#c2410c;font-weight:600">'
'{{ .Subscriber.Attribs.coupon_price_full }}</span> '
'<span style="color:#15803d">{{ .Subscriber.Attribs.coupon_price_deal }}</span>.</p>'
)
unpriced = (
'<p style="font-size:18px;font-weight:700;color:#9a3412;margin:0 0 4px">'
f'{service_phrase}.</p>'
)
expiry = "Expires {{ .Subscriber.Attribs.coupon_expires }}."
if extra_expiry_line:
expiry += " " + extra_expiry_line
return (
'{{ if .Subscriber.Attribs.coupon_code }}\n'
'<div style="background:#fff7ed;border:2px solid #f97316;border-radius:10px;'
'padding:20px;margin:20px 0;text-align:center">\n'
'<p style="font-size:13px;font-weight:700;color:#9a3412;letter-spacing:.04em;'
'margin:0 0 6px">TODAY ONLY - {{ .Subscriber.Attribs.coupon_pct }}% OFF OUR SERVICE FEE</p>\n'
'{{ if .Subscriber.Attribs.coupon_priceable }}' + priced + '{{ else }}' + unpriced + '{{ end }}\n'
'<p style="font-size:14px;color:#9a3412;margin:0 0 4px">Use code '
'<strong style="font-size:16px;letter-spacing:.08em">{{ .Subscriber.Attribs.coupon_code }}</strong> '
'(already applied when you click below).</p>\n'
f'<p style="font-size:12px;color:#b91c1c;font-weight:700;margin:0">{expiry}</p>\n'
'</div>\n'
)
# Per-campaign: the new coupon if-block (deal branch + original else branch).
BLOCKS = {
186: (
_deal_block("We file your MCS-150 update")
+ '{{ else }}\n'
'<div style="background:#f0fdf4;border:2px solid #86efac;border-radius:10px;'
'padding:20px;margin:20px 0;text-align:center">\n'
'<p style="font-size:18px;font-weight:700;color:#166534;margin:0 0 4px">'
'We can file your MCS-150 update for you.</p>\n'
'<p style="font-size:14px;color:#15803d;margin:0">No Login.gov needed. '
'No government portals. We handle everything.</p>\n'
'</div>\n'
'{{ end }}'
),
188: (
_deal_block("Reactivate your USDOT", "Reactivation takes as few as 5 business days.")
+ '{{ else }}\n'
'<div style="background:#f0fdf4;border:2px solid #86efac;border-radius:10px;'
'padding:20px;margin:20px 0;text-align:center">\n'
'<p style="font-size:18px;font-weight:700;color:#166534;margin:0 0 4px">'
'Reactivation takes as few as 5 business days.</p>\n'
'<p style="font-size:14px;color:#15803d;margin:0">We file electronically with '
'FMCSA - no Login.gov account needed.</p>\n'
'</div>\n'
'{{ end }}'
),
}
_BLOCK_RE = re.compile(
r"\{\{\s*if\s+\.Subscriber\.Attribs\.coupon_code\s*\}\}.*?\{\{\s*end\s*\}\}",
re.DOTALL,
)
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
for cid, new_block in BLOCKS.items():
camp = b.get_base_campaign(cid)
body = camp.get("body") or ""
matches = _BLOCK_RE.findall(body)
if len(matches) != 1:
print(f" [#{cid}] SKIP: found {len(matches)} coupon if-blocks (expected 1)")
continue
new_body, n = _BLOCK_RE.subn(new_block, body)
if "$47" in new_block or "$89" in new_block or "$79" in new_block or "$149" in new_block:
print(f" [#{cid}] ABORT: new block still contains a hardcoded price")
continue
# Sanity: no hardcoded coupon price survives in the rewritten coupon area.
if args.dry_run:
print(f" [#{cid}] DRY-RUN would update body ({len(body)} -> {len(new_body)} chars)")
continue
payload = {
"name": camp.get("name"),
"subject": camp.get("subject"),
"lists": [l["id"] for l in camp.get("lists", []) if isinstance(l, dict)] or [8],
"from_email": camp.get("from_email"),
"type": camp.get("type") or "regular",
"content_type": camp.get("content_type") or "html",
"body": new_body,
"altbody": camp.get("altbody"),
"template_id": camp.get("template_id") or 6,
"tags": camp.get("tags") or [],
"messenger": camp.get("messenger") or "email",
"headers": camp.get("headers") or [],
}
b.lm_api(f"/campaigns/{cid}", payload, "PUT")
print(f" [#{cid}] updated coupon block (body {len(body)} -> {len(new_body)} chars)")
return 0
if __name__ == "__main__":
sys.exit(main())