Exclude specified packages from report

Add exclude.json (12 package names, permanent) and build_data.py so the
report is reproducible. Exclusion is exact-match on package name, applied
both to 平台消耗统计 rows and to Adpingids whose 投手 pkg is excluded.
Exact match avoids false positives (e.g. in.swiggy.android vs "igg").

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
FANGKK
2026-08-26 19:06:30 +08:00
co-authored by Claude Opus 5
parent ccc7a1aa7a
commit 8d3716b726
3 changed files with 127 additions and 17 deletions
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
# 从 xlsx 重算 data.json。取数规则见 README 注释:
# 平台消耗统计: B=Date, C=Adpingid, D=包名, I=花费
# 投手: B=Adpingid, L=Media Buyer投放人
# @某某 == 某某;未填投放人的 Adpingid 丢弃;exclude.json 里的包名永久剔除。
import openpyxl, collections, json, datetime, os, sys
HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, '2608每日数据-武汉萌奇熊.xlsx')
EXCL = set(json.load(open(os.path.join(HERE, 'exclude.json')))['packages'])
NON_BUYER = {'GP下架'}
WINDOW_DAYS = 3
def key(v):
if isinstance(v, float) and v.is_integer():
return str(int(v))
return str(v).strip()
wb = openpyxl.load_workbook(SRC, data_only=True)
# --- 投手表:Adpingid -> 投放人;同时收集需按 Adping 剔除的 id ---
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]
if adp is None or str(status).strip() == '示例':
continue
k = key(adp)
if pkg is not None and str(pkg).strip() in EXCL: # 精确匹配,避免误伤 in.swiggy.android
dropped_ids.add(k)
continue
b = str(buyer).strip() if buyer is not None else ''
if b in ('', 'None') or b.lstrip('@').strip() in NON_BUYER:
continue
amap[k] = b.lstrip('@').strip()
# --- 平台消耗统计 ---
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]
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))
if not rows:
sys.exit('ERROR: 平台消耗统计 无有效数据行')
end = max(r[0] for r in rows)
start = end - datetime.timedelta(days=WINDOW_DAYS - 1)
buyers = collections.defaultdict(lambda: collections.defaultdict(float))
ids = collections.defaultdict(set)
unmatched = collections.defaultdict(float)
excluded_spend, excluded_rows = 0.0, 0
for d, adp, pkg, 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:
unmatched[(adp, pkg)] += s
out = []
for b, pk in buyers.items():
out.append({
"name": b, "@name": b,
"total": round(sum(pk.values()), 4),
"pkgs": [{"pkg": k, "spend": round(v, 4)}
for k, v in sorted(pk.items(), key=lambda x: -x[1])[:5]],
})
out.sort(key=lambda x: -x['total'])
lbl = f"{start.month}/{start.day}-{end.month}/{end.day}"
data = {
"meta": {
"week": f"Last {WINDOW_DAYS} Days: {lbl}",
"source": "数据已处理(平台消耗统计 × 投手表匹配,已剔除未填写投放人的 Adpingid 及排除包名)",
"generated_at": datetime.date.today().isoformat(),
"date_range": f"{start:%Y/%m/%d}-{end:%Y/%m/%d}",
"note": "投放人以「投手」表 Media Buyer 列为准,@某某 已与某某合并计算;"
"未填写投放人的 Adpingid 不计入;exclude.json 所列包名已排除。",
},
"grand_total": round(sum(b['total'] for b in out), 4),
"week34_id_count": sum(len(v) for v in ids.values()),
"buyers": out,
"unmatched": [{"adping": k[0], "pkg": k[1], "spend": round(v, 4)}
for k, v in sorted(unmatched.items(), key=lambda x: -x[1])],
}
json.dump(data, open(os.path.join(HERE, 'data.json'), 'w'), ensure_ascii=False, indent=2)
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}")
+7 -17
View File
@@ -1,13 +1,13 @@
{ {
"meta": { "meta": {
"week": "Last 3 Days: 8/23-8/25", "week": "Last 3 Days: 8/23-8/25",
"source": "数据已处理(平台消耗统计 × 投手表匹配,已剔除未填写投放人的 Adpingid)", "source": "数据已处理(平台消耗统计 × 投手表匹配,已剔除未填写投放人的 Adpingid 及排除包名)",
"generated_at": "2026-08-26", "generated_at": "2026-08-26",
"date_range": "2026/08/23-2026/08/25", "date_range": "2026/08/23-2026/08/25",
"note": "投放人以「投手」表 Media Buyer 列为准,@某某 已与某某合并计算;未填写投放人的 Adpingid 不计入。" "note": "投放人以「投手」表 Media Buyer 列为准,@某某 已与某某合并计算;未填写投放人的 Adpingid 不计入;exclude.json 所列包名已排除。"
}, },
"grand_total": 11160.5116, "grand_total": 11063.3116,
"week34_id_count": 121, "week34_id_count": 119,
"buyers": [ "buyers": [
{ {
"name": "黄政", "name": "黄政",
@@ -120,7 +120,7 @@
{ {
"name": "詹涵粤", "name": "詹涵粤",
"@name": "詹涵粤", "@name": "詹涵粤",
"total": 1567.5664, "total": 1470.3664,
"pkgs": [ "pkgs": [
{ {
"pkg": "com.king.candycrushsaga", "pkg": "com.king.candycrushsaga",
@@ -139,23 +139,13 @@
"spend": 80.4999 "spend": 80.4999
}, },
{ {
"pkg": "com.igg.android.lordsmobile", "pkg": "com.netease.newspike",
"spend": 63.0 "spend": 30.6
} }
] ]
} }
], ],
"unmatched": [ "unmatched": [
{
"adping": "91534913",
"pkg": "com.qookkagames.xczyw.gp.hk",
"spend": 4.2
},
{
"adping": "91533906",
"pkg": "com.sialiagames.xczyw.gp.tw",
"spend": 0.5
},
{ {
"adping": "91538334", "adping": "91538334",
"pkg": "cn.com.vtmarkets", "pkg": "cn.com.vtmarkets",
+17
View File
@@ -0,0 +1,17 @@
{
"_comment": "永久排除的包名(精确匹配,不做子串匹配)。用户 2026-08-26 指定,以后每次生成报告都不计入。",
"packages": [
"com.levelinfinite.sgameGlobal",
"com.igg.android.lordsmobile",
"net.mbc.shahid",
"water.sort.color.puzzle.offline.games",
"com.sialiagames.xczyw.gp.tw",
"com.qookkagames.xczyw.gp.hk",
"com.luizalabs.mlapp",
"br.com.netshoes.app",
"de.autodoc.gmbh",
"id891130066",
"id886443637",
"id1014949597"
]
}