ps1811 commited on
Commit
fb37220
·
1 Parent(s): 2585639

campaign doctor added

Browse files
Files changed (1) hide show
  1. app/ads1/campaign_doctor.py +101 -0
app/ads1/campaign_doctor.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import json
3
+
4
+ def build_campaign_table(dfs: dict) -> pd.DataFrame:
5
+ df = dfs["campaigns"].copy()
6
+
7
+ df["cost"] = df["cost"].fillna(0)
8
+ df["clicks"] = df["clicks"].fillna(0)
9
+ df["conversions"] = df["conversions"].fillna(0)
10
+ df["impressions"] = df["impressions"].fillna(0)
11
+
12
+ df["ctr"] = df["clicks"] / df["impressions"].replace(0, 1)
13
+ df["cpl"] = df["cost"] / df["conversions"].replace(0, 1)
14
+ df["conv_per_cost"] = df["conversions"] / df["cost"].replace(0, 1)
15
+
16
+ return df
17
+
18
+ def score_campaigns(df: pd.DataFrame) -> pd.DataFrame:
19
+ df = df.copy()
20
+
21
+ # normalize-safe scoring components
22
+ df["eff_score"] = df["conv_per_cost"]
23
+ df["eff_score"] = df["eff_score"].fillna(0)
24
+
25
+ df["cpl_score"] = 1 / df["cpl"].replace(0, 1)
26
+ df["cpl_score"] = df["cpl_score"].fillna(0)
27
+
28
+ df["volume_score"] = df["conversions"]
29
+
30
+ # final blended score (simple + stable)
31
+ df["final_score"] = (
32
+ df["eff_score"] * 0.5 +
33
+ df["cpl_score"] * 0.3 +
34
+ df["volume_score"] * 0.2
35
+ )
36
+
37
+ return df
38
+
39
+ def extract_campaign_insights(df: pd.DataFrame):
40
+ df = score_campaigns(df)
41
+
42
+ if df.empty:
43
+ return None
44
+
45
+ best = df.sort_values("final_score", ascending=False).iloc[0]
46
+ worst = df.sort_values("final_score", ascending=True).iloc[0]
47
+
48
+ # scale candidate = good efficiency but not top spender yet
49
+ scale_pool = df[
50
+ (df["conversions"] > 0)
51
+ ].sort_values("eff_score", ascending=False)
52
+
53
+ scale = scale_pool.iloc[0] if not scale_pool.empty else best
54
+
55
+ return {
56
+ "best": best.to_dict(),
57
+ "worst": worst.to_dict(),
58
+ "scale": scale.to_dict()
59
+ }
60
+
61
+ def run_campaign_doctor(dfs: dict) -> dict:
62
+ print("\n🧠 [campaign_doctor] STARTED", flush=True)
63
+
64
+ if not dfs or "campaigns" not in dfs:
65
+ return {
66
+ "best_campaign": None,
67
+ "budget_drain": None,
68
+ "scale_candidate": None
69
+ }
70
+
71
+ df = build_campaign_table(dfs)
72
+ insights = extract_campaign_insights(df)
73
+
74
+ if not insights:
75
+ return {
76
+ "best_campaign": None,
77
+ "budget_drain": None,
78
+ "scale_candidate": None
79
+ }
80
+
81
+ best = insights["best"]
82
+ worst = insights["worst"]
83
+ scale = insights["scale"]
84
+
85
+ result = {
86
+ "best_campaign": {
87
+ "name": best["name"],
88
+ "reason": "Strongest efficiency and conversion performance relative to spend"
89
+ },
90
+ "budget_drain": {
91
+ "name": worst["name"],
92
+ "reason": "High spend with weak conversion output and poor return efficiency"
93
+ },
94
+ "scale_candidate": {
95
+ "name": scale["name"],
96
+ "reason": "High conversion efficiency suggests strong potential for scaling"
97
+ }
98
+ }
99
+
100
+ print("📤 [campaign_doctor] result generated", flush=True)
101
+ return result