init: voypace ad report
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
# 一次性:从 运营/Voypace/Voypace投放计划.xlsx 的 Sheet4 生成初始 mtg_data.json
|
||||
# 之后的每日增量由 adscenter_daily_sync.py 负责。
|
||||
import openpyxl, json, os, datetime
|
||||
|
||||
XLSX = "/Users/wangyuan/Documents/SHAREit/运营/Voypace/Voypace投放计划.xlsx"
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
camps = json.load(open(os.path.join(HERE, "campaigns.json")))
|
||||
CAMPS = {r["campid"]: r for r in camps}
|
||||
# 渠道名归一:Sheet4 用 Mediago,campaigns 用 MediaGo
|
||||
CH_NORM = {"mediago": "MediaGo", "mintegral": "Mintegral", "bigo": "Bigo"}
|
||||
# 无 campid 行的兜底:按 (包名, 渠道) 反查 campid
|
||||
BY_PKG_CH = {(r["pkg"], r["channel"]): r["campid"] for r in camps}
|
||||
|
||||
base = datetime.date(1899, 12, 30)
|
||||
def to_date(v):
|
||||
if isinstance(v, (int, float)):
|
||||
return (base + datetime.timedelta(days=int(v))).isoformat()
|
||||
if isinstance(v, (datetime.datetime, datetime.date)):
|
||||
return v.strftime("%Y-%m-%d")
|
||||
return str(v)[:10]
|
||||
|
||||
def num(v):
|
||||
try: return float(str(v).replace(",", "").strip() or 0)
|
||||
except: return 0.0
|
||||
|
||||
wb = openpyxl.load_workbook(XLSX, data_only=True)
|
||||
ws = wb["Sheet4"]
|
||||
rows = list(ws.iter_rows(values_only=True))
|
||||
|
||||
cur_date = None
|
||||
out = {}
|
||||
for r in rows[1:]:
|
||||
# 列: 0时间 1单价 2地区 3包名 4OS 5AdpingID 6投放平台 7Imp 8Clk 9CTR 10Install 11CVR 12Spend 13oCPI
|
||||
if r[0] not in (None, ""):
|
||||
cur_date = to_date(r[0])
|
||||
pkg = (str(r[3]).strip() if r[3] else "")
|
||||
loc = (str(r[2]).strip().upper() if r[2] else "")
|
||||
adping = (str(r[5]).strip() if r[5] else "")
|
||||
ch_raw = (str(r[6]).strip() if r[6] else "")
|
||||
ch = CH_NORM.get(ch_raw.lower(), ch_raw)
|
||||
if not ch or not cur_date:
|
||||
continue
|
||||
# 定位 campid
|
||||
cid = adping.split(".")[0] if adping else ""
|
||||
if cid not in CAMPS:
|
||||
# 兜底:靠 包名+渠道(处理 Avito 那种 campid 为 campaign_name 的行)
|
||||
# 包名可能带 (APK)等后缀,取主体
|
||||
pkg_main = pkg.split("(")[0].split("(")[0].strip()
|
||||
cid = BY_PKG_CH.get((pkg_main, ch), "")
|
||||
if cid not in CAMPS:
|
||||
continue
|
||||
info = CAMPS[cid]
|
||||
imp = int(num(r[7])); clk = int(num(r[8])); cv = int(num(r[10])); sp = round(num(r[12]), 2)
|
||||
if not loc:
|
||||
loc = str(info.get("geo", "")).upper()
|
||||
key = (cur_date, info["channel"], cid, loc)
|
||||
a = out.get(key)
|
||||
if a:
|
||||
a["impression"] += imp; a["clicks"] += clk; a["conversions"] += cv; a["spend"] = round(a["spend"] + sp, 2)
|
||||
else:
|
||||
out[key] = {
|
||||
"date": cur_date, "channel": info["channel"], "campid": cid,
|
||||
"offer_name": f'{cid}_{info["product"]}', "product": info["product"],
|
||||
"pkg": info["pkg"], "location": loc, "bid_type": "",
|
||||
"impression": imp, "clicks": clk, "conversions": cv, "spend": sp,
|
||||
}
|
||||
|
||||
data = []
|
||||
for a in out.values():
|
||||
imp, clk, cv = a["impression"], a["clicks"], a["conversions"]
|
||||
a["ctr"] = round(clk/imp, 5) if imp else 0.0
|
||||
a["cvr"] = round(cv/clk, 5) if clk else 0.0
|
||||
data.append(a)
|
||||
data.sort(key=lambda x: (x["date"], x["channel"], -x["impression"]))
|
||||
json.dump(data, open(os.path.join(HERE, "mtg_data.json"), "w"), ensure_ascii=False, indent=2)
|
||||
print(f"生成 {len(data)} 条 -> mtg_data.json")
|
||||
Reference in New Issue
Block a user