"""Seed script for Lead Gen campaign templates.
Populates ad_templates, ad_creatives, and default optimization rules
for roofing, HVAC, and plumbing verticals.
Usage:
python scripts/seed_lead_gen_templates.py
python scripts/seed_lead_gen_templates.py --company-id <uuid>
"""
import argparse
import json
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app import create_app
from app.models import db, AdTemplate, AdCreative, OptimizationRule, Company
import uuid
# ---------------------------------------------------------------------------
# Template definitions ā real ad copy per vertical
# ---------------------------------------------------------------------------
TEMPLATES = {
"roofing": {
"emergency-repair": {
"name": "Emergency Roof Repair",
"description": "High-intent search campaign for emergency roof repair, storm damage, and leak repair. Targets homeowners in active distress.",
"budget_recommendation": 500,
"target_cpa": 50,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "Emergency Roof Repair - {{city}}",
"ad_groups": [
{
"name": "Emergency Roof Repair [{{city}}]",
"max_cpc": 3.50,
"keywords": [
{"text": "emergency roof repair", "match_type": "phrase"},
{"text": "roof leak repair near me", "match_type": "phrase"},
{"text": "storm damage roof repair", "match_type": "phrase"},
{"text": "urgent roof repair", "match_type": "phrase"},
{"text": "roof repair emergency", "match_type": "phrase"},
{"text": "emergency roofer", "match_type": "phrase"},
{"text": "roof leak emergency", "match_type": "exact"},
{"text": "emergency roof leak repair", "match_type": "phrase"},
{"text": "24 hour roof repair", "match_type": "phrase"},
{"text": "roof repair same day", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Emergency Response",
"headlines": [
"Emergency Roof Repair",
"Storm Damage? We're On The Way",
"24/7 Emergency Roof Service",
"Licensed & Insured Roofers",
"Fast Emergency Roof Repair",
"Roof Leak? Call Now",
"{{company_name}} - 24/7 Emergency",
"Same-Day Roof Repair Available",
],
"descriptions": [
"Fast response, free estimates. Call now for emergency roof repair in {{city}}.",
"Licensed, insured, 24/7 availability. Get your roof fixed today.",
"Storm damage specialists. Emergency tarping & repair. Free inspection.",
],
"call_to_action": "CALL_NOW",
}
],
},
{
"name": "Storm Damage [{{city}}]",
"max_cpc": 3.00,
"keywords": [
{"text": "storm damage roof repair", "match_type": "phrase"},
{"text": "hail damage roof", "match_type": "phrase"},
{"text": "wind damage roof repair", "match_type": "phrase"},
{"text": "insurance claim roof repair", "match_type": "phrase"},
{"text": "storm damaged roof near me", "match_type": "phrase"},
{"text": "roof inspection after storm", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Storm Damage",
"headlines": [
"Storm Damage Roof Repair",
"Free Storm Damage Inspection",
"We Work With Your Insurance",
"Hail & Wind Damage Experts",
"{{company_name}} - Storm Damage",
"Roof Damaged? We Help With Insurance",
],
"descriptions": [
"Free storm damage inspections. We handle your insurance claim start to finish.",
"Hail, wind, or water damage ā we repair it all. Licensed & insured roofers in {{city}}.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
"new-roof": {
"name": "New Roof Installation",
"description": "Consideration-stage campaign for homeowners researching new roof replacement. Higher AOV, longer sales cycle.",
"budget_recommendation": 750,
"target_cpa": 80,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "New Roof Installation - {{city}}",
"ad_groups": [
{
"name": "New Roof Replacement [{{city}}]",
"max_cpc": 4.00,
"keywords": [
{"text": "new roof installation", "match_type": "phrase"},
{"text": "roof replacement cost", "match_type": "phrase"},
{"text": "roof replacement near me", "match_type": "phrase"},
{"text": "new roof installation cost", "match_type": "phrase"},
{"text": "best roofing company", "match_type": "phrase"},
{"text": "metal roof installation", "match_type": "phrase"},
{"text": "asphalt shingle roof replacement", "match_type": "phrase"},
{"text": "roof replacement contractors", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - New Roof",
"headlines": [
"New Roof Installation",
"Free Roof Estimates",
"Quality Roof Replacement",
"{{company_name}} - Trusted Roofers",
"Roof Replacement From $X",
"Licensed Roof Contractors",
"Financing Available",
"Get Your Free Estimate Today",
],
"descriptions": [
"Get a free estimate for your new roof. Quality materials, expert installation in {{city}}.",
"Licensed, bonded, insured. 25-year workmanship warranty. Financing options available.",
],
"call_to_action": "GET_QUOTE",
}
],
},
],
},
},
"gutter-cleaning": {
"name": "Gutter Cleaning & Repair",
"description": "Seasonal campaign for gutter cleaning, repair, and gutter guard installation. Recurring revenue service.",
"budget_recommendation": 300,
"target_cpa": 30,
"bidding_strategy": "maximize_clicks",
"structure": {
"campaign_name": "Gutter Services - {{city}}",
"ad_groups": [
{
"name": "Gutter Cleaning [{{city}}]",
"max_cpc": 2.00,
"keywords": [
{"text": "gutter cleaning near me", "match_type": "phrase"},
{"text": "gutter cleaning service", "match_type": "phrase"},
{"text": "gutter cleaning cost", "match_type": "phrase"},
{"text": "gutter guard installation", "match_type": "phrase"},
{"text": "gutter repair", "match_type": "phrase"},
{"text": "gutter installation", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Gutter Cleaning",
"headlines": [
"Gutter Cleaning Service",
"Professional Gutter Cleaning",
"Gutter Guards Installed",
"{{company_name}} Gutter Services",
"Clean Gutters, Dry Basement",
"Affordable Gutter Cleaning",
],
"descriptions": [
"Professional gutter cleaning & repair in {{city}}. Free estimates, same-day service.",
"Prevent water damage with clean gutters. We also install gutter guards.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
},
"hvac": {
"ac-install": {
"name": "AC Installation",
"description": "High-intent campaign for new AC system installation. Seasonal peak in spring/summer.",
"budget_recommendation": 600,
"target_cpa": 65,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "AC Installation - {{city}}",
"ad_groups": [
{
"name": "AC Installation [{{city}}]",
"max_cpc": 4.00,
"keywords": [
{"text": "ac installation", "match_type": "phrase"},
{"text": "central air installation", "match_type": "phrase"},
{"text": "ac unit replacement", "match_type": "phrase"},
{"text": "new ac system cost", "match_type": "phrase"},
{"text": "ac installation near me", "match_type": "phrase"},
{"text": "best ac units", "match_type": "phrase"},
{"text": "heat pump installation", "match_type": "phrase"},
{"text": "hvac installation", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - AC Install",
"headlines": [
"AC Installation Experts",
"New AC System Installation",
"Energy Efficient AC Units",
"{{company_name}} HVAC",
"AC Installation - Free Estimate",
"Top-Rated HVAC Company",
"Financing Available",
"Schedule Your Free Estimate",
],
"descriptions": [
"Expert AC installation in {{city}}. Energy-efficient units, expert sizing, free estimates.",
"Carrier, Trane, Rheem certified installers. 10-year warranty. Financing available.",
],
"call_to_action": "GET_QUOTE",
}
],
},
],
},
},
"furnace-repair": {
"name": "Furnace Repair",
"description": "Seasonal campaign for furnace repair and replacement. Peak demand fall/winter.",
"budget_recommendation": 500,
"target_cpa": 45,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "Furnace Repair - {{city}}",
"ad_groups": [
{
"name": "Furnace Repair [{{city}}]",
"max_cpc": 3.50,
"keywords": [
{"text": "furnace repair", "match_type": "phrase"},
{"text": "furnace not heating", "match_type": "phrase"},
{"text": "furnace repair near me", "match_type": "phrase"},
{"text": "emergency furnace repair", "match_type": "phrase"},
{"text": "furnace replacement", "match_type": "phrase"},
{"text": "heating system repair", "match_type": "phrase"},
{"text": "no heat in house", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Furnace Repair",
"headlines": [
"Furnace Repair",
"No Heat? We'll Fix It Fast",
"Emergency Furnace Repair",
"{{company_name}} - 24/7 HVAC",
"Furnace Not Working?",
"Same-Day Furnace Repair",
"Licensed HVAC Technicians",
],
"descriptions": [
"Fast furnace repair in {{city}}. 24/7 emergency service, upfront pricing.",
"Furnace acting up? We diagnose & fix it same-day. Licensed, insured technicians.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
"seasonal-maintenance": {
"name": "Seasonal HVAC Maintenance",
"description": "Recurring revenue campaign for seasonal tune-ups and maintenance plans.",
"budget_recommendation": 250,
"target_cpa": 25,
"bidding_strategy": "maximize_clicks",
"structure": {
"campaign_name": "HVAC Maintenance - {{city}}",
"ad_groups": [
{
"name": "HVAC Tune-Up [{{city}}]",
"max_cpc": 2.00,
"keywords": [
{"text": "ac tune up", "match_type": "phrase"},
{"text": "hvac maintenance", "match_type": "phrase"},
{"text": "furnace tune up", "match_type": "phrase"},
{"text": "ac maintenance plan", "match_type": "phrase"},
{"text": "hvac service plan", "match_type": "phrase"},
{"text": "ac maintenance near me", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Maintenance",
"headlines": [
"AC Tune-Up Service",
"HVAC Maintenance Plans",
"Keep Your System Running",
"{{company_name}} Maintenance",
"Annual HVAC Tune-Up",
"Prevent Costly Repairs",
],
"descriptions": [
"Regular HVAC maintenance extends system life, lowers energy bills. Schedule yours in {{city}}.",
"Annual tune-up plans starting at $99. Priority scheduling, 15% off repairs.",
],
"call_to_action": "GET_QUOTE",
}
],
},
],
},
},
},
"plumbing": {
"drain-cleaning": {
"name": "Drain Cleaning",
"description": "High-intent campaign for drain cleaning and unclogging services. Steady demand year-round.",
"budget_recommendation": 350,
"target_cpa": 35,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "Drain Cleaning - {{city}}",
"ad_groups": [
{
"name": "Drain Cleaning [{{city}}]",
"max_cpc": 3.00,
"keywords": [
{"text": "drain cleaning", "match_type": "phrase"},
{"text": "drain cleaning near me", "match_type": "phrase"},
{"text": "clogged drain repair", "match_type": "phrase"},
{"text": "drain unclogging service", "match_type": "phrase"},
{"text": "slow drain repair", "match_type": "phrase"},
{"text": "sewer line cleaning", "match_type": "phrase"},
{"text": "main line cleaning", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Drain Cleaning",
"headlines": [
"Professional Drain Cleaning",
"Clogged Drain? We'll Fix It",
"Fast Drain Cleaning Service",
"{{company_name}} Plumbing",
"Expert Drain Unclogging",
"Same-Day Drain Cleaning",
"No Overtime Charges",
],
"descriptions": [
"Professional drain cleaning in {{city}}. Fast response, fair pricing, no overtime charges.",
"Clogged or slow drains? We use hydro-jetting and video inspection. Call now.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
"water-heater": {
"name": "Water Heater Services",
"description": "Campaign for water heater repair, replacement, and tankless installation.",
"budget_recommendation": 450,
"target_cpa": 45,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "Water Heater Services - {{city}}",
"ad_groups": [
{
"name": "Water Heater Repair [{{city}}]",
"max_cpc": 3.50,
"keywords": [
{"text": "water heater repair", "match_type": "phrase"},
{"text": "water heater replacement", "match_type": "phrase"},
{"text": "tankless water heater", "match_type": "phrase"},
{"text": "water heater installation", "match_type": "phrase"},
{"text": "no hot water", "match_type": "phrase"},
{"text": "water heater not working", "match_type": "phrase"},
{"text": "tankless water heater installation", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Water Heater",
"headlines": [
"Water Heater Repair & Install",
"No Hot Water? We're On It",
"Tankless Water Heater Install",
"{{company_name}} Plumbing",
"Water Heater Replacement",
"Same-Day Water Heater Service",
"Energy-Efficient Models",
],
"descriptions": [
"Expert water heater services in {{city}}. Repair, replacement, tankless installation.",
"Water heater acting up? Same-day service, upfront pricing. Licensed plumbers you can trust.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
"emergency-leak": {
"name": "Emergency Leak Repair",
"description": "High-urgency campaign for burst pipe and emergency leak repair. Highest CPA tolerance.",
"budget_recommendation": 400,
"target_cpa": 55,
"bidding_strategy": "target_cpa",
"structure": {
"campaign_name": "Emergency Leak Repair - {{city}}",
"ad_groups": [
{
"name": "Emergency Leak [{{city}}]",
"max_cpc": 4.50,
"keywords": [
{"text": "burst pipe repair", "match_type": "phrase"},
{"text": "emergency plumber", "match_type": "phrase"},
{"text": "emergency plumber near me", "match_type": "phrase"},
{"text": "water leak repair", "match_type": "phrase"},
{"text": "pipe leak emergency", "match_type": "phrase"},
{"text": "slab leak repair", "match_type": "phrase"},
{"text": "24 hour plumber", "match_type": "phrase"},
{"text": "emergency leak detection", "match_type": "phrase"},
],
"ads": [
{
"name": "RSA - Emergency Leak",
"headlines": [
"Emergency Leak Repair",
"Burst Pipe? Call Now",
"24/7 Emergency Plumber",
"{{company_name}} - Emergency",
"Water Leak? We're On The Way",
"Fast Emergency Plumbing",
"No Overtime Charges",
"Licensed Emergency Plumbers",
],
"descriptions": [
"Emergency plumber in {{city}}. Fast response, no overtime charges. Call 24/7.",
"Burst pipe or major leak? We're there in 60 minutes. Licensed, insured, upfront pricing.",
],
"call_to_action": "CALL_NOW",
}
],
},
],
},
},
},
}
# Default optimization rules to seed
DEFAULT_RULES = [
{
"name": "Kill High CPA Campaigns",
"rule_type": "kill_high_cpa",
"target_level": "campaign",
"params_json": {
"cpa_multiplier": 2.0,
"min_spend": 100,
"lookback_days": 14,
"action": "pause",
},
},
{
"name": "Scale Low CPA Campaigns",
"rule_type": "scale_low_cpa",
"target_level": "campaign",
"params_json": {
"cpa_multiplier": 0.7,
"min_conversions": 3,
"lookback_days": 14,
"action": "increase_budget",
"budget_increase_pct": 20,
},
},
{
"name": "Pause Zero Conversion Campaigns",
"rule_type": "pause_zero_conv",
"target_level": "campaign",
"params_json": {
"min_spend": 50,
"lookback_days": 30,
"action": "pause",
},
},
{
"name": "Kill Low CTR Keywords",
"rule_type": "kill_low_ctr",
"target_level": "keyword",
"params_json": {
"ctr_threshold": 1.0,
"min_spend": 100,
"lookback_days": 14,
"action": "pause",
},
},
{
"name": "Weekly Budget Rebalancing",
"rule_type": "budget_reallocation",
"target_level": "campaign",
"params_json": {
"schedule": "weekly",
"shift_pct": 20,
"min_campaigns": 2,
"action": "rebalance",
},
},
]
def seed_templates(company_id: str = None):
"""Seed ad templates, creatives, and default rules."""
app = create_app()
with app.app_context():
templates_added = 0
creatives_added = 0
rules_added = 0
for vertical, services in TEMPLATES.items():
for slug, config in services.items():
# Check if template already exists
existing = AdTemplate.query.filter_by(slug=slug).first()
if existing:
continue
template = AdTemplate(
id=str(uuid.uuid4()),
name=config["name"],
slug=slug,
vertical=vertical,
description=config["description"],
budget_recommendation=config["budget_recommendation"],
target_cpa=config["target_cpa"],
bidding_strategy=config["bidding_strategy"],
structure_json=config["structure"],
is_active=True,
)
db.session.add(template)
templates_added += 1
# Seed ad creatives from the template ads
for ad_group in config["structure"].get("ad_groups", []):
for ad in ad_group.get("ads", []):
# Derive service type from slug
service_type = slug.replace("-", "_")
creative = AdCreative(
id=str(uuid.uuid4()),
company_id=company_id, # nullable for system-wide templates
vertical=vertical,
service_type=service_type,
ad_type="responsive_search",
headlines=ad.get("headlines", []),
descriptions=ad.get("descriptions", []),
call_to_action=ad.get("call_to_action", "CALL_NOW"),
is_active=True,
is_winner=False,
)
db.session.add(creative)
creatives_added += 1
# Seed default optimization rules per company (requires company_id)
if company_id:
for rule_cfg in DEFAULT_RULES:
existing_rule = OptimizationRule.query.filter_by(
company_id=company_id,
rule_type=rule_cfg["rule_type"],
).first()
if existing_rule:
continue
rule = OptimizationRule(
id=str(uuid.uuid4()),
company_id=company_id,
name=rule_cfg["name"],
rule_type=rule_cfg["rule_type"],
target_level=rule_cfg["target_level"],
params_json=rule_cfg["params_json"],
is_active=True,
)
db.session.add(rule)
rules_added += 1
db.session.commit()
total_templates = len(list(TEMPLATES.values())) # approximate
print(f"\nā
Templates seeded: {templates_added}")
print(f"ā
Creatives seeded: {creatives_added}")
print(f"ā
Optimization rules seeded: {rules_added}")
if company_id:
print(f" ā Scoped to company: {company_id}")
else:
print(" ā System-wide (company_id=NULL, rules skipped)")
# List all templates
all_templates = AdTemplate.query.order_by(AdTemplate.vertical, AdTemplate.slug).all()
print(f"\nš All templates in DB ({len(all_templates)} total):")
for t in all_templates:
budget_str = f"${t.budget_recommendation:.0f}/mo" if t.budget_recommendation else "N/A"
cpa_str = f"${t.target_cpa:.0f}" if t.target_cpa else "N/A"
print(f" [{t.vertical}] {t.slug} ā {t.name} (budget: {budget_str}, CPA: {cpa_str})")
def main():
parser = argparse.ArgumentParser(description="Seed lead gen templates")
parser.add_argument(
"--company-id",
type=str,
default=None,
help="Company ID to scope templates to (default: system-wide)",
)
args = parser.parse_args()
seed_templates(args.company_id)
if __name__ == "__main__":
main()