Add Geo suffix and Adpingid column to 投手明细

Package now renders as pkg_GEO, where GEO is the region with the highest
total spend for that package. The new Adpingid column shows the
top-spending Adping within that region, so label and id stay consistent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
FANGKK
2026-08-27 14:39:38 +08:00
co-authored by Claude Opus 5
parent 06f701699e
commit fcf9a5c01b
3 changed files with 126 additions and 23 deletions
+48 -20
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env python3
# 从 xlsx 重算 data.json。取数规则见 README 注释:
# 平台消耗统计: B=Date, C=Adpingid, D=包名, I=花费
# 投手: B=Adpingid, L=Media Buyer投放人
# @某某 == 某某;未填投放人的 Adpingid 丢弃;exclude.json 里的包名永久剔除。
# 从 xlsx 重算 data.json。
# 平台消耗统计: B=Date, C=Adpingid, D=包名, E=地区, I=花费
# 投手: B=Adpingid, D=pkg, L=Media Buyer投放人
# 规则:@某某 == 某某;未填投放人的 Adpingid 丢弃;exclude.json 里的包名永久剔除(精确匹配)。
# 每个 package 明细:Geo 后缀 = 该 package 花费最高的地区;Adpingid = 该地区内花费最高的 Adping。
import openpyxl, collections, json, datetime, os, sys
HERE = os.path.dirname(os.path.abspath(__file__))
@@ -18,7 +19,7 @@ def key(v):
wb = openpyxl.load_workbook(SRC, data_only=True)
# --- 投手表:Adpingid -> 投放人;同时收集需按 Adping 剔除的 id ---
# --- 投手表:Adpingid -> 投放人 ---
amap, dropped_ids = {}, set()
for r in wb['投手'].iter_rows(min_row=2, values_only=True):
adp, pkg, buyer, status = r[1], r[3], r[11], r[0]
@@ -38,14 +39,15 @@ owned = collections.Counter(amap.values()) # 每位投手名下的 Adpingid
# --- 平台消耗统计 ---
rows = []
for r in wb['平台消耗统计'].iter_rows(min_row=2, values_only=True):
d, adp, pkg, spend = r[1], r[2], r[3], r[8]
d, adp, pkg, geo, spend = r[1], r[2], r[3], r[4], r[8]
if d is None or adp is None or not isinstance(d, datetime.datetime):
continue
try:
s = float(spend) if spend is not None else 0.0
except (TypeError, ValueError):
s = 0.0
rows.append((d.date(), key(adp), (str(pkg).strip() if pkg else '(未知)'), s))
rows.append((d.date(), key(adp), (str(pkg).strip() if pkg else '(未知)'),
(str(geo).strip() if geo else ''), s))
if not rows:
sys.exit('ERROR: 平台消耗统计 无有效数据行')
@@ -53,32 +55,55 @@ if not rows:
end = max(r[0] for r in rows)
start = end - datetime.timedelta(days=WINDOW_DAYS - 1)
buyers = collections.defaultdict(lambda: collections.defaultdict(float))
pkg_spend = collections.defaultdict(float) # (buyer, pkg) -> spend
geo_spend = collections.defaultdict(float) # (buyer, pkg, geo) -> spend
ga_spend = collections.defaultdict(float) # (buyer, pkg, geo, adping) -> spend
ids = collections.defaultdict(set)
unmatched = collections.defaultdict(float)
excluded_spend, excluded_rows = 0.0, 0
for d, adp, pkg, s in rows:
for d, adp, pkg, geo, s in rows:
if not (start <= d <= end):
continue
if pkg in EXCL or adp in dropped_ids:
excluded_spend += s; excluded_rows += 1
continue
if adp in amap:
buyers[amap[adp]][pkg] += s
ids[amap[adp]].add(adp)
else:
b = amap.get(adp)
if b is None:
unmatched[(adp, pkg)] += s
continue
pkg_spend[(b, pkg)] += s
geo_spend[(b, pkg, geo)] += s
ga_spend[(b, pkg, geo, adp)] += s
ids[b].add(adp)
by_buyer = collections.defaultdict(dict)
for (b, pkg), v in pkg_spend.items():
by_buyer[b][pkg] = v
out = []
for b, pk in buyers.items():
for b, pk in by_buyer.items():
pkgs = []
for pkg, v in sorted(pk.items(), key=lambda x: -x[1])[:5]:
# 花费最高的地区,再取该地区内花费最高的 Adpingid
top_geo = max((g for (bb, pp, g) in geo_spend if bb == b and pp == pkg),
key=lambda g: geo_spend[(b, pkg, g)])
top_adp = max((a for (bb, pp, g, a) in ga_spend
if bb == b and pp == pkg and g == top_geo),
key=lambda a: ga_spend[(b, pkg, top_geo, a)])
pkgs.append({
"pkg": pkg,
"geo": top_geo,
"label": f"{pkg}_{top_geo}" if top_geo else pkg,
"adping": top_adp,
"spend": round(v, 4),
})
out.append({
"name": b, "@name": b,
"total": round(sum(pk.values()), 4),
"active_ids": len(ids[b]), # 本周期内有消耗的 Adpingid 数
"owned_ids": owned[b], # 名下负责的 Adpingid 总数
"pkgs": [{"pkg": k, "spend": round(v, 4)}
for k, v in sorted(pk.items(), key=lambda x: -x[1])[:5]],
"active_ids": len(ids[b]),
"owned_ids": owned[b],
"pkgs": pkgs,
})
out.sort(key=lambda x: -x['total'])
@@ -90,7 +115,8 @@ data = {
"generated_at": datetime.date.today().isoformat(),
"date_range": f"{start:%Y/%m/%d}-{end:%Y/%m/%d}",
"note": "投放人以「投手」表 Media Buyer 列为准,@某某 已与某某合并计算;"
"未填写投放人的 Adpingid 不计入;exclude.json 所列包名已排除。",
"未填写投放人的 Adpingid 不计入;exclude.json 所列包名已排除。"
"Package 后缀为该 Package 花费最高的投放地区,Adpingid 为该地区内花费最高的 Adping。",
},
"grand_total": round(sum(b['total'] for b in out), 4),
"week34_id_count": sum(len(v) for v in ids.values()),
@@ -104,4 +130,6 @@ print(f"窗口 {start} -> {end}")
print(f"排除包名命中 {excluded_rows} 行,剔除花费 {excluded_spend:.2f}")
print(f"合计 {data['grand_total']:.2f} / Adpingid {data['week34_id_count']}")
for b in out:
print(f" {b['name']:6s} {b['total']:10.2f}")
print(f" {b['name']:6s} {b['total']:10.2f} 在投{b['active_ids']:3d}/负责{b['owned_ids']:3d}")
for p in b['pkgs']:
print(f" {p['label']:52s} {p['adping']:>10s} {p['spend']:9.2f}")