Script 305: Monthly Budget Cap Data Feed
In a Nutshell
The script automatically pauses PPC campaigns that exceed their monthly budget caps.
To Elaborate
- The script enforces budget caps for PPC campaigns based on data from Google Sheets.
- It identifies campaigns that have spent more than their allocated budget and pauses them to prevent overspending.
- The script is designed to be run periodically to ensure budgets are not exceeded.
Walking Through the Code
- Budget caps are no longer hardcoded; they are retrieved from a Google Sheets data feed.
- The script defines several constants for column names used in reports and bulk operations.
- It processes a dataframe (
inputDf
) containing campaign data, including costs and statuses. - The script groups campaign data by strategy to calculate total spend per strategy.
- It merges the total spend data with budget caps to identify strategies that are over budget.
- Campaigns are then checked against both strategy-level and campaign-level caps.
- Campaigns that exceed either cap are marked to be paused.
- The script generates an output dataframe (
outputDf
) with the campaigns that need their status changed. - If there are campaigns to be paused, the script prints the changes; otherwise, it indicates that no changes are needed.
- The script also includes a TODO comment about reactivating campaigns when a new month starts if they are below the cap.
Vitals
- Script ID : 305
- Client ID / Customer ID: 309909744 / 14196
- Action Type: Bulk Upload (Preview)
- Item Changed: Campaign
- Output Columns: Account, Campaign, Status
- Created by Michael Huang on 2023-09-18 07:43:19
- Last Updated by Michael Huang on 2023-12-06 04:01:47
> See it in Action
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#
# Auto Pause Campaigns based on Monthly Budget Caps uploaded via Data Feed
#
# TODO: Reactivate Campaigns paused by script when new month starts (cost < cap)
# * tag SCRIPTS_PAUSED with date
# * remove tag on new month
# * reactivate if below cap and has tag
#
# Author: Michael S. Huang
# Created: 2023-09-18
#
########### Configurable Params - START ##########
# Update: Now using GSheets to provide caps
# # define budget cap rules
# strategy_campaign_budget_cap = {
# # format:
# # 'strategy name': (overall-strategy cap, campaign-in-strategy cap)
# 'Unassigned': (100000, 7000),
# 'Criteo Retail': (25000, 2000),
# 'Amazon: Sponsored Products': (5000, 1000),
# 'TikTok - CPA': (25000, 2000),
# 'Cross Channel Budget Pacing': (10000, 1000),
# }
########### Configurable Params - END ###########
RPT_COL_CAMPAIGN = 'Campaign'
RPT_COL_ACCOUNT = 'Account'
RPT_COL_STRATEGY = 'Strategy'
RPT_COL_CAMPAIGN_STATUS = 'Campaign Status'
RPT_COL_PUB_COST = 'Pub. Cost $'
BULK_COL_ACCOUNT = 'Account'
BULK_COL_CAMPAIGN = 'Campaign'
BULK_COL_STATUS = 'Status'
VAL_STATUS_ACTIVE = 'Active'
VAL_STATUS_PAUSED = 'Paused'
COL_STRATEGY_CAP = 'strategy_cap'
COL_STRATEGY_CAMPAIGN_CAP = 'campaign_cap'
outputDf[BULK_COL_STATUS] = "<<YOUR VALUE>>"
# # convert budget cap into dataframe
# df_budget_caps = pd.DataFrame.from_dict(strategy_campaign_budget_cap, orient='index') \
# .reset_index()
# get budget cap from Data Feed
df_budget_caps = dataSourceDict["2"]
df_budget_caps.columns = [RPT_COL_STRATEGY, COL_STRATEGY_CAP, COL_STRATEGY_CAMPAIGN_CAP]
print("budget caps", tableize(df_budget_caps))
# reduce inputDf to needed columns
cols = [RPT_COL_ACCOUNT, RPT_COL_CAMPAIGN, RPT_COL_STRATEGY, RPT_COL_CAMPAIGN_STATUS, RPT_COL_PUB_COST]
inputDf = inputDf[cols].copy()
# create temp column to store new status and default to empty
TMP_STATUS = RPT_COL_CAMPAIGN_STATUS + '_'
inputDf[TMP_STATUS] = np.nan
# calculate total spend for each Strategy
df_strategy_agg = inputDf.groupby(RPT_COL_STRATEGY).agg({RPT_COL_PUB_COST:'sum'})
print("== Strategy-level Spend ==", df_strategy_agg.to_string())
# put above together to determine Strategies over budget cap
df_strategy_agg_with_cap = df_strategy_agg.merge(df_budget_caps, on=[RPT_COL_STRATEGY]).reset_index()
print("strategy agg with cap", tableize(df_strategy_agg_with_cap))
strategies_over_cap = df_strategy_agg_with_cap.loc[ \
df_strategy_agg_with_cap[RPT_COL_PUB_COST] >= df_strategy_agg_with_cap[COL_STRATEGY_CAP], \
RPT_COL_STRATEGY \
]
# similarly, combine budget cap with campaigns to determine campaigns over cap
df_campaigns_with_cap = inputDf.merge(df_budget_caps, on=[RPT_COL_STRATEGY], how='left').reset_index()
print("campaigns with cap", tableize(df_campaigns_with_cap))
# determine which campaigns are over Strategy Cap or Campaign Cap
campaigns_over_cap = (df_campaigns_with_cap[RPT_COL_STRATEGY].isin(strategies_over_cap)) | \
(df_campaigns_with_cap[RPT_COL_PUB_COST] >= df_campaigns_with_cap[COL_STRATEGY_CAMPAIGN_CAP])
# Pause all camapigns over Strategy Cap or Campaign Cap
inputDf.loc[ campaigns_over_cap, TMP_STATUS ] = VAL_STATUS_PAUSED
# find changed campaigns
changed = inputDf[TMP_STATUS].notnull() & (inputDf[RPT_COL_CAMPAIGN_STATUS] != inputDf[TMP_STATUS])
if sum(changed) > 0:
print("== Campaigns with Changed Status ==", tableize(inputDf.loc[changed]))
# only select changed rows
cols = [RPT_COL_ACCOUNT, RPT_COL_CAMPAIGN, TMP_STATUS]
outputDf = inputDf.loc[ changed, cols ].copy() \
.rename(columns = { \
TMP_STATUS: BULK_COL_STATUS \
})
print("outputDf", tableize(outputDf))
else:
print("Empty outputDf")
outputDf = outputDf.iloc[0:0]
Post generated on 2024-02-22 06:05:49 GMT