build_trucking_campaigns: add --max-per-segment / --only-segment / --send-hour for fresh-IP warmup sends

Lets us fire small, controlled batches (e.g. MCS-150 only, 100/tz, sent today)
while the new sending IPs warm up, instead of the full multi-segment schedule.
This commit is contained in:
justin 2026-06-02 12:30:08 -05:00
parent 98bcf0bbb0
commit fd3ceb3efc

View file

@ -365,7 +365,9 @@ def list_segments(send_date: date) -> None:
conn.close()
def run(send_date: date, dry_run: bool = False, preview: bool = False) -> None:
def run(send_date: date, dry_run: bool = False, preview: bool = False,
max_per_segment: int | None = None, only_segments: set[str] | None = None,
send_hour_override: int | None = None, send_minute: int = 0) -> None:
conn = psycopg2.connect(DB_URL)
base_mcs150 = get_base_campaign(CAMPAIGN_MCS150_ID)
@ -391,16 +393,30 @@ def run(send_date: date, dry_run: bool = False, preview: bool = False) -> None:
continue
campaign_specs.append((ctype, base, seg["limit"], seg["label"]))
# Optional warmup controls: restrict to specific segments and/or cap the
# per-segment audience so a fresh-IP day-0 send stays small.
if only_segments:
campaign_specs = [s for s in campaign_specs if s[0] in only_segments]
if not campaign_specs:
LOG.error("No campaign specs match --only-segment %s", sorted(only_segments))
conn.close()
return
if max_per_segment is not None:
campaign_specs = [
(ct, base, min(lim, max_per_segment), label)
for (ct, base, lim, label) in campaign_specs
]
if preview:
LOG.info("PREVIEW MODE — 1 sample carrier per campaign, drafts only, "
"test sends to %s, no real schedule, no mark-sent.", TEST_EMAIL)
for tz, tz_cfg in TIMEZONE_CONFIG.items():
states = tz_cfg["states"]
send_hour = tz_cfg["send_hour_utc"]
send_hour = send_hour_override if send_hour_override is not None else tz_cfg["send_hour_utc"]
send_at = datetime(
send_date.year, send_date.month, send_date.day,
send_hour, 0, 0, tzinfo=timezone.utc
send_hour, send_minute, 0, tzinfo=timezone.utc
)
for campaign_type, base, limit, label in campaign_specs:
@ -478,6 +494,14 @@ def main():
parser.add_argument("--preview", action="store_true", help="1 sample carrier/campaign, drafts only, test sends to owner, no real schedule/mark-sent")
parser.add_argument("--list-segments", action="store_true", help="Report deduped audience size per deficiency segment, then exit (no writes)")
parser.add_argument("--date", type=str, help="Target send date YYYY-MM-DD (default: tomorrow)")
parser.add_argument("--max-per-segment", type=int, default=None,
help="Cap audience per (segment x timezone). Use for fresh-IP warmup small sends.")
parser.add_argument("--only-segment", action="append", default=None, metavar="SEG",
help="Restrict to specific segment(s), e.g. mcs150, inactive, for_hire_boc3. Repeatable.")
parser.add_argument("--send-hour", type=int, default=None,
help="Override send hour (UTC) for ALL timezones instead of per-tz defaults.")
parser.add_argument("--send-minute", type=int, default=0,
help="Send minute (UTC), used with --send-hour. Default 0.")
args = parser.parse_args()
if args.date:
@ -489,8 +513,14 @@ def main():
list_segments(send_date)
return
LOG.info("Building campaigns for send date %s (dry_run=%s, preview=%s)", send_date, args.dry_run, args.preview)
run(send_date, dry_run=args.dry_run, preview=args.preview)
only = set(args.only_segment) if args.only_segment else None
LOG.info("Building campaigns for send date %s (dry_run=%s, preview=%s, "
"max_per_segment=%s, only=%s, send_hour=%s)",
send_date, args.dry_run, args.preview, args.max_per_segment,
sorted(only) if only else None, args.send_hour)
run(send_date, dry_run=args.dry_run, preview=args.preview,
max_per_segment=args.max_per_segment, only_segments=only,
send_hour_override=args.send_hour, send_minute=args.send_minute)
if __name__ == "__main__":