Add KPI event and CAC data pipeline
This commit is contained in:
+134
-62
@@ -31,7 +31,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "与10599157 under在同一个客户下",
|
"remark": "与10599157 under在同一个客户下",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "af_level_achieved, af_purchase",
|
"kpi": "af_purchase",
|
||||||
"remark": "0812 新上",
|
"remark": "0812 新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "af_level_achieved, af_purchase",
|
"kpi": "af_purchase",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "af_level_achieved, af_purchase",
|
"kpi": "af_purchase",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
"geo": "IN",
|
"geo": "IN",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -150,8 +150,8 @@
|
|||||||
"owner": "黄政",
|
"owner": "黄政",
|
||||||
"geo": "IN",
|
"geo": "IN",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "对照组和实验组用的同一个adping,人群包campaign没有新开adpingid给武汉",
|
"remark": "对照组和实验组用的同一个adping,人群包campaign没有新开adpingid给武汉",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
"geo": "PH",
|
"geo": "PH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "对照组和实验组用的同一个adping,人群包campaign没有新开adpingid给武汉",
|
"remark": "对照组和实验组用的同一个adping,人群包campaign没有新开adpingid给武汉",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
"geo": "PH",
|
"geo": "PH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "只有实验组",
|
"remark": "只有实验组",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -235,7 +235,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Pause",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
"geo": "PH",
|
"geo": "PH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "8/14 还没上",
|
"remark": "8/14 还没上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "8/14 还没上",
|
"remark": "8/14 还没上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "8/14 还没上",
|
"remark": "8/14 还没上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
"geo": "MY",
|
"geo": "MY",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "8/14 还没上",
|
"remark": "8/14 还没上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -319,7 +319,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -343,7 +343,7 @@
|
|||||||
"geo": "PH",
|
"geo": "PH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -379,7 +379,7 @@
|
|||||||
"geo": "MY",
|
"geo": "MY",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -414,8 +414,8 @@
|
|||||||
"owner": "周梓君",
|
"owner": "周梓君",
|
||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "new_buyer>3%",
|
"kpi": "new_buyer",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -439,7 +439,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "new_buyer>3%",
|
"kpi": "new_buyer",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "new_buyer>3%",
|
"kpi": "new_buyer",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -463,7 +463,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "new_buyer>3%",
|
"kpi": "new_buyer",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -475,7 +475,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -487,7 +487,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "idsp - 无事件要求",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -619,7 +619,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "game.levelup",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -631,7 +631,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "game.levelup",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -643,7 +643,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -655,7 +655,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -667,7 +667,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -679,7 +679,7 @@
|
|||||||
"geo": "TH",
|
"geo": "TH",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -691,7 +691,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "CPI订单,Install=New devicesKPI:new users>50%, cvr>0.5%(soft); 可用log in,play tracks, purchase事件优化质量",
|
"kpi": "login",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -703,7 +703,7 @@
|
|||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "CPI订单,Install=New devicesKPI:new users>50%, cvr>0.5%(soft); 可用log in,play tracks, purchase事件优化质量",
|
"kpi": "login",
|
||||||
"remark": "0826新上",
|
"remark": "0826新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -715,7 +715,7 @@
|
|||||||
"geo": "AE",
|
"geo": "AE",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -727,7 +727,7 @@
|
|||||||
"geo": "ZA",
|
"geo": "ZA",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -739,7 +739,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "BIGO",
|
"platform": "BIGO",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "bigo"
|
"channel": "bigo"
|
||||||
},
|
},
|
||||||
@@ -751,7 +751,7 @@
|
|||||||
"geo": "AE",
|
"geo": "AE",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -763,7 +763,7 @@
|
|||||||
"geo": "ZA",
|
"geo": "ZA",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -775,7 +775,7 @@
|
|||||||
"geo": "BR",
|
"geo": "BR",
|
||||||
"platform": "BIGO",
|
"platform": "BIGO",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "event1",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "bigo"
|
"channel": "bigo"
|
||||||
},
|
},
|
||||||
@@ -783,11 +783,11 @@
|
|||||||
"pkg": "com.esprit.rpg.lttlefirykng",
|
"pkg": "com.esprit.rpg.lttlefirykng",
|
||||||
"adping_id": "11655596",
|
"adping_id": "11655596",
|
||||||
"group": "对照组",
|
"group": "对照组",
|
||||||
"owner": "",
|
"owner": "万科",
|
||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "BIGO",
|
"platform": "BIGO",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "game.levelup",
|
||||||
"remark": "0827新上",
|
"remark": "0827新上",
|
||||||
"channel": "bigo"
|
"channel": "bigo"
|
||||||
},
|
},
|
||||||
@@ -795,11 +795,11 @@
|
|||||||
"pkg": "com.esprit.rpg.lttlefirykng",
|
"pkg": "com.esprit.rpg.lttlefirykng",
|
||||||
"adping_id": "11763482",
|
"adping_id": "11763482",
|
||||||
"group": "实验组",
|
"group": "实验组",
|
||||||
"owner": "",
|
"owner": "万科",
|
||||||
"geo": "RU",
|
"geo": "RU",
|
||||||
"platform": "BIGO",
|
"platform": "BIGO",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "game.levelup",
|
||||||
"remark": "0827新上",
|
"remark": "0827新上",
|
||||||
"channel": "bigo"
|
"channel": "bigo"
|
||||||
},
|
},
|
||||||
@@ -811,7 +811,7 @@
|
|||||||
"geo": "MY",
|
"geo": "MY",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0828新上",
|
"remark": "0828新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -823,7 +823,7 @@
|
|||||||
"geo": "MY",
|
"geo": "MY",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "af_purchase",
|
||||||
"remark": "0828新上",
|
"remark": "0828新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -834,8 +834,8 @@
|
|||||||
"owner": "",
|
"owner": "",
|
||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Pause",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -847,7 +847,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "",
|
"remark": "",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -859,7 +859,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "0831新上",
|
"remark": "0831新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -871,7 +871,7 @@
|
|||||||
"geo": "ID",
|
"geo": "ID",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "0831新上",
|
"remark": "0831新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -883,7 +883,7 @@
|
|||||||
"geo": "IN",
|
"geo": "IN",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "0901新上",
|
"remark": "0901新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
},
|
},
|
||||||
@@ -895,8 +895,80 @@
|
|||||||
"geo": "IN",
|
"geo": "IN",
|
||||||
"platform": "MTG",
|
"platform": "MTG",
|
||||||
"status": "Active",
|
"status": "Active",
|
||||||
"kpi": "",
|
"kpi": "-",
|
||||||
"remark": "0901新上",
|
"remark": "0901新上",
|
||||||
"channel": "mintegral"
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "sports.caliente.mx.calientedeportes",
|
||||||
|
"adping_id": "5365722",
|
||||||
|
"group": "对照组",
|
||||||
|
"owner": "詹涵粤",
|
||||||
|
"geo": "MX",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "singup",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "sports.caliente.mx.calientedeportes",
|
||||||
|
"adping_id": "11924654",
|
||||||
|
"group": "实验组",
|
||||||
|
"owner": "詹涵粤",
|
||||||
|
"geo": "MX",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "singup",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "astrum.ent.m2alyona",
|
||||||
|
"adping_id": "12038500",
|
||||||
|
"group": "实验组",
|
||||||
|
"owner": "谭超",
|
||||||
|
"geo": "RU",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "af_purchase",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "astrum.ent.m2alyona",
|
||||||
|
"adping_id": "11690181",
|
||||||
|
"group": "对照组",
|
||||||
|
"owner": "谭超",
|
||||||
|
"geo": "RU",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "af_purchase",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "asrtum.ent.domovyata",
|
||||||
|
"adping_id": "12038344",
|
||||||
|
"group": "实验组",
|
||||||
|
"owner": "黄政",
|
||||||
|
"geo": "RU",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "af_purchase",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "asrtum.ent.domovyata",
|
||||||
|
"adping_id": "7872826",
|
||||||
|
"group": "对照组",
|
||||||
|
"owner": "黄政",
|
||||||
|
"geo": "RU",
|
||||||
|
"platform": "MTG",
|
||||||
|
"status": "Active",
|
||||||
|
"kpi": "af_purchase",
|
||||||
|
"remark": "",
|
||||||
|
"channel": "mintegral"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
+3
-3
@@ -52,11 +52,11 @@ function updateDateBounds(reset=false){const months=selected('month'),rows=ancho
|
|||||||
function updateAnchoredFilters(resetDates=false){const rows=anchorRows();multiOptions('month',rows.map(r=>r.date.slice(0,7)));singleOptions('geo',rows.map(r=>r.geo));singleOptions('source',rows.map(r=>r.source));multiOptions('group',rows.map(r=>r.group));updateDateBounds(resetDates)}
|
function updateAnchoredFilters(resetDates=false){const rows=anchorRows();multiOptions('month',rows.map(r=>r.date.slice(0,7)));singleOptions('geo',rows.map(r=>r.geo));singleOptions('source',rows.map(r=>r.source));multiOptions('group',rows.map(r=>r.group));updateDateBounds(resetDates)}
|
||||||
function filtered(){const q=$('search').value.trim().toLowerCase(),months=selected('month'),groups=selected('group');return all.filter(r=>r.status==='Active'&&(!$('pkg').value||r.pkg===$('pkg').value)&&(!months.length||months.includes(r.date.slice(0,7)))&&(!$('geo').value||r.geo===$('geo').value)&&(!$('source').value||r.source===$('source').value)&&(!groups.length||groups.includes(r.group))&&(!$('start').value||r.date>=$('start').value)&&(!$('end').value||r.date<=$('end').value)&&(!q||(`${r.campaign} ${r.adpingId}`).toLowerCase().includes(q)))}
|
function filtered(){const q=$('search').value.trim().toLowerCase(),months=selected('month'),groups=selected('group');return all.filter(r=>r.status==='Active'&&(!$('pkg').value||r.pkg===$('pkg').value)&&(!months.length||months.includes(r.date.slice(0,7)))&&(!$('geo').value||r.geo===$('geo').value)&&(!$('source').value||r.source===$('source').value)&&(!groups.length||groups.includes(r.group))&&(!$('start').value||r.date>=$('start').value)&&(!$('end').value||r.date<=$('end').value)&&(!q||(`${r.campaign} ${r.adpingId}`).toLowerCase().includes(q)))}
|
||||||
const tracksEvent=r=>{const k=String(r.kpi||'').trim();return k&&k!=='-'};
|
const tracksEvent=r=>{const k=String(r.kpi||'').trim();return k&&k!=='-'};
|
||||||
function aggregate(rows,keys){const m=new Map;for(const r of rows){const k=keys.map(x=>r[x]).join('\u0001');if(!m.has(k))m.set(k,{...Object.fromEntries(keys.map(x=>[x,r[x]])),impression:0,click:0,conversions:0,spend:0,event:0,eventKnown:false,kpi:r.kpi||''});const o=m.get(k);for(const x of ['impression','click','conversions','spend'])o[x]+=+r[x]||0;if(r.event!==null&&r.event!==undefined&&r.event!==''){o.event+=+r.event||0;o.eventKnown=true}}return [...m.values()]}
|
function aggregate(rows,keys){const m=new Map;for(const r of rows){const k=keys.map(x=>r[x]).join('\u0001');if(!m.has(k))m.set(k,{...Object.fromEntries(keys.map(x=>[x,r[x]])),impression:0,click:0,conversions:0,spend:0,event:0,eventSpend:0,eventKnown:false,kpi:r.kpi||''});const o=m.get(k);for(const x of ['impression','click','conversions','spend'])o[x]+=+r[x]||0;if(r.event!==null&&r.event!==undefined&&r.event!==''){o.event+=+r.event||0;o.eventSpend+=+r.spend||0;o.eventKnown=true}}return [...m.values()]}
|
||||||
function cards(rows){const t=aggregate(rows,[])[0]||{impression:0,click:0,conversions:0,spend:0},e=aggregate(rows.filter(tracksEvent),[])[0]||{event:0,eventKnown:false,spend:0};const vals=[['Spend',money(t.spend)],['Event',e.eventKnown?n(e.event):'—'],['CAC',e.eventKnown&&e.event?money(e.spend/e.event):'—'],['Conversions',n(t.conversions)],['CPI',t.conversions?money(t.spend/t.conversions):'—'],['Impression',n(t.impression)],['Click',n(t.click)],['CTR',pct(rate(t.click,t.impression))],['CVR',pct(rate(t.conversions,t.click))]];$('cards').innerHTML=vals.map(x=>`<div class="panel card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div></div>`).join('')}
|
function cards(rows){const t=aggregate(rows,[])[0]||{impression:0,click:0,conversions:0,spend:0},e=aggregate(rows.filter(tracksEvent),[])[0]||{event:0,eventKnown:false,eventSpend:0};const vals=[['Spend',money(t.spend)],['Event',e.eventKnown?n(e.event):'—'],['CAC',e.eventKnown&&e.event?money(e.eventSpend/e.event):'—'],['Conversions',n(t.conversions)],['CPI',t.conversions?money(t.spend/t.conversions):'—'],['Impression',n(t.impression)],['Click',n(t.click)],['CTR',pct(rate(t.click,t.impression))],['CVR',pct(rate(t.conversions,t.click))]];$('cards').innerHTML=vals.map(x=>`<div class="panel card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div></div>`).join('')}
|
||||||
function compact(v,prefix=''){return prefix+Intl.NumberFormat('en-US',{notation:'compact',maximumFractionDigits:1}).format(v)}
|
function compact(v,prefix=''){return prefix+Intl.NumberFormat('en-US',{notation:'compact',maximumFractionDigits:1}).format(v)}
|
||||||
function trend(rows){const d=aggregate(rows,['date']).sort((a,b)=>a.date.localeCompare(b.date)),svg=$('trend'),tip=$('trendTip');if(!d.length){svg.innerHTML='<text x="450" y="135" text-anchor="middle" class="axis-label">当前筛选没有数据</text>';return}const W=900,H=270,L=62,R=66,T=20,B=38,maxS=Math.max(...d.map(x=>x.spend),1),maxC=Math.max(...d.map(x=>x.conversions),1),x=i=>L+(W-L-R)*(d.length===1?.5:i/(d.length-1)),y=(v,max)=>T+(H-T-B)*(1-v/max),path=(key,max)=>d.map((r,i)=>(i?'L':'M')+x(i).toFixed(1)+' '+y(r[key],max).toFixed(1)).join(' ');let parts=[];for(let i=0;i<=4;i++){const yy=T+(H-T-B)*i/4,ratio=1-i/4;parts.push(`<line class="gridline" x1="${L}" y1="${yy}" x2="${W-R}" y2="${yy}"/><text class="axis-label" x="${L-8}" y="${yy+4}" text-anchor="end">${compact(maxS*ratio,'$')}</text><text class="axis-label" x="${W-R+8}" y="${yy+4}">${compact(maxC*ratio)}</text>`)}const labelStep=Math.max(1,Math.ceil(d.length/8));parts.push(...d.map((r,i)=>(i%labelStep===0||i===d.length-1)?`<text class="axis-label" x="${x(i)}" y="${H-12}" text-anchor="middle">${r.date.slice(5)}</text>`:''));parts.push(`<text class="axis-title" x="${L}" y="11" fill="#1769e0">Spend (USD)</text><text class="axis-title" x="${W-R}" y="11" text-anchor="end" fill="#16825d">Conversions</text>`);if(trendVisible.spend)parts.push(`<path class="spend-line" d="${path('spend',maxS)}"/>`);if(trendVisible.conversions)parts.push(`<path class="conv-line" d="${path('conversions',maxC)}"/>`);parts.push(...d.map((r,i)=>`<g class="trend-hit" data-index="${i}"><circle cx="${x(i)}" cy="${trendVisible.spend?y(r.spend,maxS):y(r.conversions,maxC)}" r="12" fill="transparent"/><circle class="point" cx="${x(i)}" cy="${trendVisible.spend?y(r.spend,maxS):y(r.conversions,maxC)}" r="3.5" fill="${trendVisible.spend?'#1769e0':'#16825d'}"/></g>`));svg.innerHTML=parts.join('');svg.querySelectorAll('.trend-hit').forEach(el=>{el.addEventListener('mouseenter',()=>{const r=d[+el.dataset.index];tip.innerHTML=`<strong>${r.date}</strong><br>Spend:${money(r.spend)}<br>Conversions:${n(r.conversions)}`;tip.style.display='block'});el.addEventListener('mousemove',e=>{const box=svg.getBoundingClientRect(),panel=svg.parentElement.getBoundingClientRect();tip.style.left=Math.min(e.clientX-panel.left+12,panel.width-tip.offsetWidth-12)+'px';tip.style.top=(e.clientY-panel.top-tip.offsetHeight-10)+'px'});el.addEventListener('mouseleave',()=>tip.style.display='none')})}
|
function trend(rows){const d=aggregate(rows,['date']).sort((a,b)=>a.date.localeCompare(b.date)),svg=$('trend'),tip=$('trendTip');if(!d.length){svg.innerHTML='<text x="450" y="135" text-anchor="middle" class="axis-label">当前筛选没有数据</text>';return}const W=900,H=270,L=62,R=66,T=20,B=38,maxS=Math.max(...d.map(x=>x.spend),1),maxC=Math.max(...d.map(x=>x.conversions),1),x=i=>L+(W-L-R)*(d.length===1?.5:i/(d.length-1)),y=(v,max)=>T+(H-T-B)*(1-v/max),path=(key,max)=>d.map((r,i)=>(i?'L':'M')+x(i).toFixed(1)+' '+y(r[key],max).toFixed(1)).join(' ');let parts=[];for(let i=0;i<=4;i++){const yy=T+(H-T-B)*i/4,ratio=1-i/4;parts.push(`<line class="gridline" x1="${L}" y1="${yy}" x2="${W-R}" y2="${yy}"/><text class="axis-label" x="${L-8}" y="${yy+4}" text-anchor="end">${compact(maxS*ratio,'$')}</text><text class="axis-label" x="${W-R+8}" y="${yy+4}">${compact(maxC*ratio)}</text>`)}const labelStep=Math.max(1,Math.ceil(d.length/8));parts.push(...d.map((r,i)=>(i%labelStep===0||i===d.length-1)?`<text class="axis-label" x="${x(i)}" y="${H-12}" text-anchor="middle">${r.date.slice(5)}</text>`:''));parts.push(`<text class="axis-title" x="${L}" y="11" fill="#1769e0">Spend (USD)</text><text class="axis-title" x="${W-R}" y="11" text-anchor="end" fill="#16825d">Conversions</text>`);if(trendVisible.spend)parts.push(`<path class="spend-line" d="${path('spend',maxS)}"/>`);if(trendVisible.conversions)parts.push(`<path class="conv-line" d="${path('conversions',maxC)}"/>`);parts.push(...d.map((r,i)=>`<g class="trend-hit" data-index="${i}"><circle cx="${x(i)}" cy="${trendVisible.spend?y(r.spend,maxS):y(r.conversions,maxC)}" r="12" fill="transparent"/><circle class="point" cx="${x(i)}" cy="${trendVisible.spend?y(r.spend,maxS):y(r.conversions,maxC)}" r="3.5" fill="${trendVisible.spend?'#1769e0':'#16825d'}"/></g>`));svg.innerHTML=parts.join('');svg.querySelectorAll('.trend-hit').forEach(el=>{el.addEventListener('mouseenter',()=>{const r=d[+el.dataset.index];tip.innerHTML=`<strong>${r.date}</strong><br>Spend:${money(r.spend)}<br>Conversions:${n(r.conversions)}`;tip.style.display='block'});el.addEventListener('mousemove',e=>{const box=svg.getBoundingClientRect(),panel=svg.parentElement.getBoundingClientRect();tip.style.left=Math.min(e.clientX-panel.left+12,panel.width-tip.offsetWidth-12)+'px';tip.style.top=(e.clientY-panel.top-tip.offsetHeight-10)+'px'});el.addEventListener('mouseleave',()=>tip.style.display='none')})}
|
||||||
function table(rows){const d=aggregate(rows,['date','group','campaign','kpi']).sort((a,b)=>b.date.localeCompare(a.date)||(a.group==='对照组'?-1:1)-(b.group==='对照组'?-1:1)||b.spend-a.spend);$('count').textContent=`${n(d.length)} 行`;const limited=d.slice(0,2000),dateCounts={};for(const r of limited)dateCounts[r.date]=(dateCounts[r.date]||0)+1;const seen=new Set;$('body').innerHTML=limited.map(r=>{const first=!seen.has(r.date),required=tracksEvent(r);seen.add(r.date);return`<tr class="${first?'date-start':''}">${first?`<td class="date-cell" rowspan="${dateCounts[r.date]}">${r.date}</td>`:''}<td class="${r.group==='对照组'?'control':'experiment'}">${r.group}</td><td>${r.campaign}</td><td>${r.kpi||'-'}</td><td class="num">${required&&r.eventKnown?n(r.event):'—'}</td><td class="num">${money(r.spend)}</td><td class="num">${required&&r.eventKnown&&r.event?money(r.spend/r.event):'—'}</td><td class="num">${n(r.impression)}</td><td class="num">${n(r.click)}</td><td class="num">${n(r.conversions)}</td><td class="num">${pct(rate(r.click,r.impression))}</td><td class="num">${pct(rate(r.conversions,r.click))}</td><td class="num">${r.conversions?money(r.spend/r.conversions):'—'}</td></tr>`}).join('')||'<tr><td colspan="13" class="empty">当前筛选没有数据</td></tr>'}
|
function table(rows){const d=aggregate(rows,['date','group','campaign','kpi']).sort((a,b)=>b.date.localeCompare(a.date)||(a.group==='对照组'?-1:1)-(b.group==='对照组'?-1:1)||b.spend-a.spend);$('count').textContent=`${n(d.length)} 行`;const limited=d.slice(0,2000),dateCounts={};for(const r of limited)dateCounts[r.date]=(dateCounts[r.date]||0)+1;const seen=new Set;$('body').innerHTML=limited.map(r=>{const first=!seen.has(r.date),required=tracksEvent(r);seen.add(r.date);return`<tr class="${first?'date-start':''}">${first?`<td class="date-cell" rowspan="${dateCounts[r.date]}">${r.date}</td>`:''}<td class="${r.group==='对照组'?'control':'experiment'}">${r.group}</td><td>${r.campaign}</td><td>${r.kpi||'-'}</td><td class="num">${required&&r.eventKnown?n(r.event):'—'}</td><td class="num">${money(r.spend)}</td><td class="num">${required&&r.eventKnown&&r.event?money(r.eventSpend/r.event):'—'}</td><td class="num">${n(r.impression)}</td><td class="num">${n(r.click)}</td><td class="num">${n(r.conversions)}</td><td class="num">${pct(rate(r.click,r.impression))}</td><td class="num">${pct(rate(r.conversions,r.click))}</td><td class="num">${r.conversions?money(r.spend/r.conversions):'—'}</td></tr>`}).join('')||'<tr><td colspan="13" class="empty">当前筛选没有数据</td></tr>'}
|
||||||
function render(){lastRows=filtered();cards(lastRows);trend(lastRows);table(lastRows)}
|
function render(){lastRows=filtered();cards(lastRows);trend(lastRows);table(lastRows)}
|
||||||
function toggleTrend(key,id){trendVisible[key]=!trendVisible[key];if(!trendVisible.spend&&!trendVisible.conversions)trendVisible[key]=true;$(id).classList.toggle('off',!trendVisible[key]);$(id).setAttribute('aria-pressed',trendVisible[key]);trend(lastRows)}
|
function toggleTrend(key,id){trendVisible[key]=!trendVisible[key];if(!trendVisible.spend&&!trendVisible.conversions)trendVisible[key]=true;$(id).classList.toggle('off',!trendVisible[key]);$(id).setAttribute('aria-pressed',trendVisible[key]);trend(lastRows)}
|
||||||
$('toggleSpend').addEventListener('click',()=>toggleTrend('spend','toggleSpend'));$('toggleConversions').addEventListener('click',()=>toggleTrend('conversions','toggleConversions'));
|
$('toggleSpend').addEventListener('click',()=>toggleTrend('spend','toggleSpend'));$('toggleConversions').addEventListener('click',()=>toggleTrend('conversions','toggleConversions'));
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import urllib.request
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from tableauhyperapi import Connection, HyperProcess, Telemetry
|
||||||
|
|
||||||
|
|
||||||
|
SERVER = os.environ.get("TABLEAU_SERVER", "https://mars.ushareit.org").rstrip("/")
|
||||||
|
API_VERSION = os.environ.get("TABLEAU_API_VERSION", "3.15")
|
||||||
|
SITE_CONTENT_URL = os.environ.get("TABLEAU_SITE_CONTENT_URL", "")
|
||||||
|
WORKBOOK_ID = os.environ.get("TABLEAU_EVENT_WORKBOOK_ID", "ad567777-aa52-4c2f-89c3-5acfed4fdbba")
|
||||||
|
|
||||||
|
|
||||||
|
def request_json(url, *, body=None, headers=None):
|
||||||
|
request = urllib.request.Request(url, data=body, headers=headers or {})
|
||||||
|
with urllib.request.urlopen(request, timeout=120) as response:
|
||||||
|
return json.load(response)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
raise SystemExit("usage: refresh_events.py OUTPUT.csv")
|
||||||
|
output_path = sys.argv[1]
|
||||||
|
pat_name = os.environ.get("TABLEAU_PAT_NAME", "")
|
||||||
|
pat_secret = os.environ.get("TABLEAU_PAT_SECRET", "")
|
||||||
|
if not pat_name or not pat_secret:
|
||||||
|
raise SystemExit("TABLEAU_PAT_NAME and TABLEAU_PAT_SECRET are required")
|
||||||
|
|
||||||
|
signin_body = json.dumps({"credentials": {
|
||||||
|
"personalAccessTokenName": pat_name,
|
||||||
|
"personalAccessTokenSecret": pat_secret,
|
||||||
|
"site": {"contentUrl": SITE_CONTENT_URL},
|
||||||
|
}}).encode()
|
||||||
|
signin = request_json(
|
||||||
|
f"{SERVER}/api/{API_VERSION}/auth/signin",
|
||||||
|
body=signin_body,
|
||||||
|
headers={"Content-Type": "application/json", "Accept": "application/json"},
|
||||||
|
)["credentials"]
|
||||||
|
auth_token = signin["token"]
|
||||||
|
site_id = signin["site"]["id"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="manta-events-") as temp_dir:
|
||||||
|
archive_path = Path(temp_dir) / "workbook.twbx"
|
||||||
|
download = urllib.request.Request(
|
||||||
|
f"{SERVER}/api/{API_VERSION}/sites/{site_id}/workbooks/{WORKBOOK_ID}/content",
|
||||||
|
headers={"X-Tableau-Auth": auth_token},
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(download, timeout=180) as response:
|
||||||
|
archive_path.write_bytes(response.read())
|
||||||
|
with zipfile.ZipFile(archive_path) as archive:
|
||||||
|
members = [name for name in archive.namelist() if name.endswith(".hyper")]
|
||||||
|
if len(members) != 1:
|
||||||
|
raise SystemExit(f"expected one Hyper extract, found {len(members)}")
|
||||||
|
archive.extract(members[0], temp_dir)
|
||||||
|
hyper_path = Path(temp_dir) / members[0]
|
||||||
|
query = '''
|
||||||
|
SELECT "dt", "pkg_name", "adping_id", "offer_name", "event_name",
|
||||||
|
SUM("event_num")
|
||||||
|
FROM "Extract"."Extract"
|
||||||
|
GROUP BY "dt", "pkg_name", "adping_id", "offer_name", "event_name"
|
||||||
|
ORDER BY "dt", "pkg_name", "offer_name", "event_name"
|
||||||
|
'''
|
||||||
|
with HyperProcess(Telemetry.DO_NOT_SEND_USAGE_DATA_TO_TABLEAU) as process:
|
||||||
|
with Connection(process.endpoint, str(hyper_path)) as connection:
|
||||||
|
rows = connection.execute_list_query(query)
|
||||||
|
with open(output_path, "w", encoding="utf-8", newline="") as handle:
|
||||||
|
writer = csv.writer(handle)
|
||||||
|
writer.writerow(("dt", "pkg_name", "adping_id", "offer_name", "event_name", "event_num"))
|
||||||
|
writer.writerows(rows)
|
||||||
|
dates = sorted({str(row[0]) for row in rows})
|
||||||
|
print(json.dumps({"rows": len(rows), "dates": dates}))
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
signout = urllib.request.Request(
|
||||||
|
f"{SERVER}/api/{API_VERSION}/auth/signout", data=b"",
|
||||||
|
headers={"X-Tableau-Auth": auth_token},
|
||||||
|
)
|
||||||
|
urllib.request.urlopen(signout, timeout=30).close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+27
-1
@@ -3,7 +3,10 @@ import csv, io, json, os, re, sys
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
src, target = sys.argv[1:]
|
if len(sys.argv) not in (3, 4):
|
||||||
|
raise SystemExit('usage: transform_tableau.py SPEND.csv YYYY-MM-DD [EVENTS.csv]')
|
||||||
|
src, target = sys.argv[1:3]
|
||||||
|
events_path = sys.argv[3] if len(sys.argv) == 4 else None
|
||||||
raw = open(src, 'rb').read()
|
raw = open(src, 'rb').read()
|
||||||
if b'<html' in raw[:1000].lower():
|
if b'<html' in raw[:1000].lower():
|
||||||
raise SystemExit('Tableau authentication redirected to HTML')
|
raise SystemExit('Tableau authentication redirected to HTML')
|
||||||
@@ -33,6 +36,22 @@ def num(v):
|
|||||||
try: return float(str(v or '0').replace(',', '').replace('%', ''))
|
try: return float(str(v or '0').replace(',', '').replace('%', ''))
|
||||||
except ValueError: return 0
|
except ValueError: return 0
|
||||||
|
|
||||||
|
def event_names(value):
|
||||||
|
value = str(value or '').strip()
|
||||||
|
if not value or value == '-': return []
|
||||||
|
return [x.strip().lower() for x in re.split(r'[,,;/;\n]+', value) if x.strip()]
|
||||||
|
|
||||||
|
events, covered_dates = {}, set()
|
||||||
|
if events_path:
|
||||||
|
with open(events_path, encoding='utf-8-sig', newline='') as f:
|
||||||
|
for event_row in csv.DictReader(f):
|
||||||
|
dt = re.sub(r'[^0-9]', '', event_row.get('dt', ''))
|
||||||
|
if len(dt) == 8: dt = f'{dt[:4]}-{dt[4:6]}-{dt[6:]}'
|
||||||
|
covered_dates.add(dt)
|
||||||
|
key = (dt, event_row.get('pkg_name','').strip(), event_row.get('offer_name','').strip(),
|
||||||
|
event_row.get('event_name','').strip().lower())
|
||||||
|
events[key] = events.get(key, 0) + num(event_row.get('event_num'))
|
||||||
|
|
||||||
config = [r for r in json.load(open('campaign_config.json', encoding='utf-8')) if r['status'].lower() == 'active']
|
config = [r for r in json.load(open('campaign_config.json', encoding='utf-8')) if r['status'].lower() == 'active']
|
||||||
experiment = {}
|
experiment = {}
|
||||||
for r in config:
|
for r in config:
|
||||||
@@ -66,8 +85,15 @@ for raw_row in matrix[1:] if has_header else matrix:
|
|||||||
unresolved.append(campaign)
|
unresolved.append(campaign)
|
||||||
continue
|
continue
|
||||||
group, shown, adping = '对照组', 'General campaign', ''
|
group, shown, adping = '对照组', 'General campaign', ''
|
||||||
|
date = cell(raw_row, 'date').strip() or target
|
||||||
|
required = event_names(meta.get('kpi',''))
|
||||||
|
if not required or source != 'mintegral' or date not in covered_dates:
|
||||||
|
event = None
|
||||||
|
else:
|
||||||
|
event = sum(events.get((date, pkg, campaign, name), 0) for name in required)
|
||||||
rows.append({'date': cell(raw_row, 'date').strip() or target, 'pkg':pkg, 'geo':meta['geo'], 'source':source,
|
rows.append({'date': cell(raw_row, 'date').strip() or target, 'pkg':pkg, 'geo':meta['geo'], 'source':source,
|
||||||
'group':group, 'adpingId':adping, 'campaign':shown, 'status':'Active', 'kpi':meta.get('kpi',''),
|
'group':group, 'adpingId':adping, 'campaign':shown, 'status':'Active', 'kpi':meta.get('kpi',''),
|
||||||
|
'event':event,
|
||||||
'impression':num(cell(raw_row,'impression')), 'click':num(cell(raw_row,'click')),
|
'impression':num(cell(raw_row,'impression')), 'click':num(cell(raw_row,'click')),
|
||||||
'conversions':num(cell(raw_row,'conversions')), 'spend':num(cell(raw_row,'spend'))})
|
'conversions':num(cell(raw_row,'conversions')), 'spend':num(cell(raw_row,'spend'))})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user