init: voypace ad report

This commit is contained in:
王源
2026-09-10 15:26:54 +08:00
commit 3c525a9672
9 changed files with 1833 additions and 0 deletions
+77
View File
@@ -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")