PKG板块:按pkg/account可选呈现+pkg/account筛选+排序; 客户分类板块加排序; 数据改为pkg×account明细

This commit is contained in:
2026-09-11 17:08:11 +08:00
parent 0cd8d81f47
commit 088e38cda1
3 changed files with 259 additions and 65 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+242 -51
View File
@@ -33,6 +33,16 @@
.dod-hi-neg{background:#fee2e2;color:#dc2626;font-weight:600;}
.sub-note{color:var(--sub);font-size:12px;margin:4px 2px 10px;}
.total-row td{font-weight:700;background:#eef2f7;}
.ctrl{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-bottom:10px;}
.ctrl label{font-size:13px;cursor:pointer;user-select:none;}
.ctrl input[type=text]{border:1px solid var(--line);border-radius:6px;padding:5px 8px;font-size:12px;min-width:150px;}
th.sortable{cursor:pointer;user-select:none;}
th.sortable:hover{background:#374151;}
th .arw{display:inline-block;width:9px;margin-left:3px;font-size:10px;color:#93c5fd;}
th.asc .arw::after{content:"\25B2";} th.desc .arw::after{content:"\25BC";}
tr.acc-sub td{background:#f3f6fb;color:#475569;font-size:12px;}
tr.acc-sub td.txt{padding-left:26px;}
tr.acc-sub:hover td{background:#e8eef7;}
</style>
</head>
<body>
@@ -67,12 +77,24 @@
<div id="tab-pkg" class="hide">
<div class="card">
<h2>2.1 PKG 收入波动 - Midas <span class="tag">保留14天</span></h2>
<div class="sub-note">DoD=最新日与前一日收入差;|DoD|&gt;100 标注,+绿 −红。波动原因可编辑(暂存本浏览器)。</div>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="midas" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="midas"> 按 account</label>
<input type="text" class="f-pkg" data-t="midas" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="midas" placeholder="筛选 account">
<span class="sub-note" style="margin:0;">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器</span>
</div>
<div class="scroll"><table id="tPkgMidas"><thead></thead><tbody></tbody></table></div>
</div>
<div class="card">
<h2>2.2 PKG 收入波动 - MS <span class="tag">保留14天</span></h2>
<div class="sub-note">DoD=最新日与前一日收入差;|DoD|&gt;100 标注,+绿 −红。波动原因可编辑(暂存本浏览器)。</div>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="ms" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="ms"> 按 account</label>
<input type="text" class="f-pkg" data-t="ms" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="ms" placeholder="筛选 account">
<span class="sub-note" style="margin:0;">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器</span>
</div>
<div class="scroll"><table id="tPkgMS"><thead></thead><tbody></tbody></table></div>
</div>
</div>
@@ -83,14 +105,26 @@
<h2>3.1 客户分类日报</h2>
<div class="sub-note">DoD=T-1−T-2,DoD%=T-1/T-2−1。波动原因可编辑(暂存本浏览器)。</div>
<div class="scroll"><table id="tCustDaily"><thead><tr>
<th class="txt">客户分类</th><th>iDSP个数</th><th>base daily revenue</th>
<th>T-4收入</th><th>T-3收入</th><th>T-2收入</th><th>T-1收入</th><th>DoD</th><th>DoD%</th><th class="txt">波动原因</th>
<th class="txt cd-sort" data-k="cat" data-ty="str">客户分类<span class="arw"></span></th>
<th class="cd-sort" data-k="idsp" data-ty="num">iDSP个数<span class="arw"></span></th>
<th class="cd-sort" data-k="base" data-ty="num">base daily revenue<span class="arw"></span></th>
<th class="cd-sort" data-k="t4" data-ty="num">T-4收入<span class="arw"></span></th>
<th class="cd-sort" data-k="t3" data-ty="num">T-3收入<span class="arw"></span></th>
<th class="cd-sort" data-k="t2" data-ty="num">T-2收入<span class="arw"></span></th>
<th class="cd-sort" data-k="t1" data-ty="num">T-1收入<span class="arw"></span></th>
<th class="cd-sort" data-k="dod" data-ty="num">DoD<span class="arw"></span></th>
<th class="cd-sort" data-k="dodp" data-ty="num">DoD%<span class="arw"></span></th>
<th class="txt">波动原因</th>
</tr></thead><tbody></tbody></table></div>
</div>
<div class="card">
<h2>3.2 客户明细 <span class="tag">保留14天</span></h2>
<div class="scroll"><table id="tCustDetail"><thead><tr>
<th class="txt">客户分类</th><th class="txt">客户</th><th>base收入</th><th>T-1收入</th><th>DoD(与T-2差)</th>
<th class="txt de-sort" data-k="cat" data-ty="str">客户分类<span class="arw"></span></th>
<th class="txt de-sort" data-k="acc" data-ty="str">客户<span class="arw"></span></th>
<th class="de-sort" data-k="base" data-ty="num">base收入<span class="arw"></span></th>
<th class="de-sort" data-k="t1" data-ty="num">T-1收入<span class="arw"></span></th>
<th class="de-sort" data-k="dod" data-ty="num">DoD(与T-2差)<span class="arw"></span></th>
</tr></thead><tbody></tbody></table></div>
</div>
</div>
@@ -157,82 +191,237 @@ function renderAccTable(tblId, rowsData, totalKey){
document.querySelector('#'+tblId+' tbody').innerHTML=body;
}
// ===== 2.1/2.2 PKG波动 (pkg为行, dt为列, 末列DoD+波动原因) =====
function renderPkg(tblId, byDt, noteTbl){
var dts=sortedDates(byDt); // 升序
var showDts=dts.slice(-14);
// ===== 2.1/2.2 PKG波动 =====
// byDt: {dt: {pkg: {account: rev}}}
// 模式: byPkg / byAcc 两个复选框;排序: 按 latest(最新日收入) 或 dod
var PKG_STATE={
midas:{tbl:'tPkgMidas', note:'midas', byPkg:true, byAcc:false, fPkg:'', fAcc:'', sort:'latest', dir:'desc'},
ms:{tbl:'tPkgMS', note:'ms', byPkg:true, byAcc:false, fPkg:'', fAcc:'', sort:'latest', dir:'desc'}
};
function pkgDatesFor(byDt){ return sortedDates(byDt).slice(-14); }
// 聚合出:pkg -> {dt: total}, pkg -> {acc: {dt: rev}}
function buildPkgIndex(byDt, showDts){
var pkgTotal={}; // pkg -> {dt: sum}
var pkgAcc={}; // pkg -> {acc: {dt: rev}}
showDts.forEach(function(dt){
var m=byDt[dt]||{};
Object.keys(m).forEach(function(pkg){
var accs=m[pkg]||{};
Object.keys(accs).forEach(function(acc){
var v=accs[acc]||0;
(pkgTotal[pkg]=pkgTotal[pkg]||{})[dt]=((pkgTotal[pkg][dt]||0)+v);
((pkgAcc[pkg]=pkgAcc[pkg]||{})[acc]=pkgAcc[pkg][acc]||{})[dt]=v;
});
});
});
return {pkgTotal:pkgTotal, pkgAcc:pkgAcc};
}
// account视角: acc -> {dt: sum(over pkg)}
function buildAccIndex(byDt, showDts){
var accTotal={};
showDts.forEach(function(dt){
var m=byDt[dt]||{};
Object.keys(m).forEach(function(pkg){
var accs=m[pkg]||{};
Object.keys(accs).forEach(function(acc){
(accTotal[acc]=accTotal[acc]||{})[dt]=((accTotal[acc][dt]||0)+(accs[acc]||0));
});
});
});
return accTotal;
}
function dodOf(map,latest,prev){ return (map[latest]||0)-(prev?(map[prev]||0):0); }
function dodCls(dod){ if(Math.abs(dod)>100) return dod>0?'dod-hi-pos':'dod-hi-neg'; return dod>=0?'pos':'neg'; }
function renderPkg(which){
var st=PKG_STATE[which]; var byDt=(which==='midas'?D.pkg_midas:D.pkg_ms);
var showDts=pkgDatesFor(byDt);
var latest=showDts[showDts.length-1], prev=showDts[showDts.length-2];
// 收集所有pkg
var pkgset={};
showDts.forEach(function(dt){ Object.keys(byDt[dt]).forEach(function(p){pkgset[p]=1;}); });
var pkgs=Object.keys(pkgset);
// 按最新日收入降序
pkgs.sort(function(a,b){ return (byDt[latest][b]||0)-(byDt[latest][a]||0); });
var thead=document.querySelector('#'+tblId+' thead');
var th='<tr><th class="txt sticky-col">PKG</th>';
var thead=document.querySelector('#'+st.tbl+' thead');
var tbody=document.querySelector('#'+st.tbl+' tbody');
var fp=st.fPkg.trim().toLowerCase(), fa=st.fAcc.trim().toLowerCase();
function sortArr(arr, valLatest, valDod){
var dir=st.dir==='asc'?1:-1;
arr.sort(function(a,b){
var va=st.sort==='dod'?valDod(a):valLatest(a);
var vb=st.sort==='dod'?valDod(b):valLatest(b);
return (va<vb?-1:va>vb?1:0)*dir;
});
return arr;
}
function headRow(firstCol){
var th='<tr><th class="txt sticky-col">'+firstCol+'</th>';
showDts.forEach(function(dt){ th+='<th>'+dt+'</th>'; });
th+='<th>DoD</th><th class="txt">波动原因</th></tr>';
thead.innerHTML=th;
var body='';
var la=(st.sort==='latest'?(' '+st.dir):''), da=(st.sort==='dod'?(' '+st.dir):'');
th+='<th class="sortable'+la+'" data-sort="latest" data-t="'+which+'">最新收入<span class="arw"></span></th>';
th+='<th class="sortable'+da+'" data-sort="dod" data-t="'+which+'">DoD<span class="arw"></span></th>';
th+='<th class="txt">波动原因</th></tr>';
return th;
}
var html='';
if(st.byPkg){
var idx=buildPkgIndex(byDt,showDts);
var pkgs=Object.keys(idx.pkgTotal);
if(fp) pkgs=pkgs.filter(function(p){return p.toLowerCase().indexOf(fp)>=0;});
if(fa) pkgs=pkgs.filter(function(p){ return Object.keys(idx.pkgAcc[p]||{}).some(function(a){return a.toLowerCase().indexOf(fa)>=0;}); });
thead.innerHTML=headRow('PKG');
sortArr(pkgs, function(p){return idx.pkgTotal[p][latest]||0;}, function(p){return dodOf(idx.pkgTotal[p],latest,prev);});
pkgs.forEach(function(p){
var tot=idx.pkgTotal[p];
var row='<tr><td class="txt sticky-col">'+p+'</td>';
showDts.forEach(function(dt){ var v=byDt[dt][p]; row+='<td>'+(v==null?'':n2(v))+'</td>'; });
var lv=byDt[latest]&&byDt[latest][p]||0, pv=(prev&&byDt[prev]&&byDt[prev][p])||0;
var dod=lv-pv;
var cls='';
if(Math.abs(dod)>100){ cls = dod>0?'dod-hi-pos':'dod-hi-neg'; }
else { cls = dod>=0?'pos':'neg'; }
row+='<td class="'+cls+'">'+n2(dod)+'</td>';
row+=reasonCell(noteTbl, p);
row+='</tr>';
body+=row;
showDts.forEach(function(dt){ var v=tot[dt]; row+='<td>'+(v==null?'':n2(v))+'</td>'; });
var dod=dodOf(tot,latest,prev);
row+='<td>'+n2(tot[latest]||0)+'</td><td class="'+dodCls(dod)+'">'+n2(dod)+'</td>'+reasonCell(st.note,p)+'</tr>';
html+=row;
// 两者都选: 该pkg下各account子行
if(st.byAcc){
var accs=idx.pkgAcc[p]||{};
var alist=Object.keys(accs);
if(fa) alist=alist.filter(function(a){return a.toLowerCase().indexOf(fa)>=0;});
sortArr(alist, function(a){return accs[a][latest]||0;}, function(a){return dodOf(accs[a],latest,prev);});
alist.forEach(function(a){
var m=accs[a]; var sub='<tr class="acc-sub"><td class="txt">↳ '+a+'</td>';
showDts.forEach(function(dt){ var v=m[dt]; sub+='<td>'+(v==null?'':n2(v))+'</td>'; });
var d2=dodOf(m,latest,prev);
sub+='<td>'+n2(m[latest]||0)+'</td><td class="'+dodCls(d2)+'">'+n2(d2)+'</td><td></td></tr>';
html+=sub;
});
}
});
} else if(st.byAcc){
// 仅按account
var accTotal=buildAccIndex(byDt,showDts);
var accs=Object.keys(accTotal);
if(fa) accs=accs.filter(function(a){return a.toLowerCase().indexOf(fa)>=0;});
// fp在纯account模式下无意义,忽略
thead.innerHTML=headRow('account');
sortArr(accs, function(a){return accTotal[a][latest]||0;}, function(a){return dodOf(accTotal[a],latest,prev);});
accs.forEach(function(a){
var tot=accTotal[a];
var row='<tr><td class="txt sticky-col">'+a+'</td>';
showDts.forEach(function(dt){ var v=tot[dt]; row+='<td>'+(v==null?'':n2(v))+'</td>'; });
var dod=dodOf(tot,latest,prev);
row+='<td>'+n2(tot[latest]||0)+'</td><td class="'+dodCls(dod)+'">'+n2(dod)+'</td>'+reasonCell(st.note+'_acc',a)+'</tr>';
html+=row;
});
} else {
// 都没勾,默认回到按pkg
st.byPkg=true; return renderPkg(which);
}
tbody.innerHTML=html;
}
// PKG 控制交互
function bindPkgControls(){
document.querySelectorAll('.m-pkg').forEach(function(el){ el.onchange=function(){ PKG_STATE[el.getAttribute('data-t')].byPkg=el.checked; renderPkg(el.getAttribute('data-t')); }; });
document.querySelectorAll('.m-acc').forEach(function(el){ el.onchange=function(){ PKG_STATE[el.getAttribute('data-t')].byAcc=el.checked; renderPkg(el.getAttribute('data-t')); }; });
document.querySelectorAll('.f-pkg').forEach(function(el){ el.oninput=function(){ PKG_STATE[el.getAttribute('data-t')].fPkg=el.value; renderPkg(el.getAttribute('data-t')); }; });
document.querySelectorAll('.f-acc').forEach(function(el){ el.oninput=function(){ PKG_STATE[el.getAttribute('data-t')].fAcc=el.value; renderPkg(el.getAttribute('data-t')); }; });
// 表头排序(事件委托)
document.addEventListener('click',function(e){
var th=e.target.closest && e.target.closest('th.sortable');
if(!th || !th.getAttribute('data-t')) return;
var which=th.getAttribute('data-t'), s=th.getAttribute('data-sort'), st=PKG_STATE[which];
if(st.sort===s){ st.dir=(st.dir==='asc'?'desc':'asc'); } else { st.sort=s; st.dir='desc'; }
renderPkg(which);
});
document.querySelector('#'+tblId+' tbody').innerHTML=body;
}
// ===== 3.1 客户分类日报 =====
var CD_SORT={k:null,ty:'num',dir:'desc'};
function renderCustDaily(){
var dts=sortedDates(D.cust_daily); // 升序
var t1=dts[dts.length-1], t2=dts[dts.length-2], t3=dts[dts.length-3], t4=dts[dts.length-4];
var cats=Object.keys(D.cust_idsp);
// base = 该分类各account base之和
var baseByCat={};
(D.cust_base||[]).forEach(function(x){ baseByCat[x.cat]=(baseByCat[x.cat]||0)+(+x.base||0); });
var body='';
cats.forEach(function(cat){
var rows=cats.map(function(cat){
function val(dt){ return dt&&D.cust_daily[dt]? (D.cust_daily[dt][cat]||0):null; }
var v1=val(t1),v2=val(t2),v3=val(t3),v4=val(t4);
var dod=(v1!=null&&v2!=null)?v1-v2:null;
var dodp=(v1!=null&&v2)?(v1/v2-1):null;
var dc = dod==null?'':(dod>=0?'pos':'neg');
body+='<tr><td class="txt">'+cat+'</td><td>'+(D.cust_idsp[cat]||'')+'</td><td>'+n2(baseByCat[cat])+'</td>'
+'<td>'+(v4==null?'':n2(v4))+'</td><td>'+(v3==null?'':n2(v3))+'</td><td>'+(v2==null?'':n2(v2))+'</td>'
+'<td class="pos">'+(v1==null?'':n2(v1))+'</td>'
+'<td class="'+dc+'">'+(dod==null?'':n2(dod))+'</td>'
+'<td class="'+dc+'">'+(dodp==null?'':pct(dodp))+'</td>'
+reasonCell('cust_daily', cat)+'</tr>';
return {cat:cat,idsp:D.cust_idsp[cat]||0,base:baseByCat[cat]||0,t4:v4,t3:v3,t2:v2,t1:v1,dod:dod,dodp:dodp};
});
if(CD_SORT.k){
var dir=CD_SORT.dir==='asc'?1:-1;
rows.sort(function(a,b){
if(CD_SORT.ty==='str') return dir*String(a[CD_SORT.k]).localeCompare(String(b[CD_SORT.k]));
var va=a[CD_SORT.k]==null?-Infinity:a[CD_SORT.k], vb=b[CD_SORT.k]==null?-Infinity:b[CD_SORT.k];
return (va<vb?-1:va>vb?1:0)*dir;
});
}
var body=rows.map(function(r){
var dc=r.dod==null?'':(r.dod>=0?'pos':'neg');
return '<tr><td class="txt">'+r.cat+'</td><td>'+(r.idsp||'')+'</td><td>'+n2(r.base)+'</td>'
+'<td>'+(r.t4==null?'':n2(r.t4))+'</td><td>'+(r.t3==null?'':n2(r.t3))+'</td><td>'+(r.t2==null?'':n2(r.t2))+'</td>'
+'<td class="pos">'+(r.t1==null?'':n2(r.t1))+'</td>'
+'<td class="'+dc+'">'+(r.dod==null?'':n2(r.dod))+'</td>'
+'<td class="'+dc+'">'+(r.dodp==null?'':pct(r.dodp))+'</td>'
+reasonCell('cust_daily', r.cat)+'</tr>';
}).join('');
document.querySelector('#tCustDaily tbody').innerHTML=body;
document.querySelectorAll('#tCustDaily th.cd-sort').forEach(function(th){
th.classList.remove('asc','desc');
if(th.getAttribute('data-k')===CD_SORT.k) th.classList.add(CD_SORT.dir);
});
}
// ===== 3.2 客户明细 =====
var DE_SORT={k:null,ty:'num',dir:'desc'};
function renderCustDetail(){
var dts=sortedDates(D.cust_detail);
var t1=dts[dts.length-1], t2=dts[dts.length-2];
var t1list=D.cust_detail[t1]||[];
var t2map={};
(D.cust_detail[t2]||[]).forEach(function(x){ t2map[x.cat+'||'+x.acc]=x.t1; });
// 按分类、再按T-1收入降序
var rows=t1list.slice().sort(function(a,b){ return a.cat<b.cat?-1:a.cat>b.cat?1:(b.t1-a.t1); });
var body='';
rows.forEach(function(r){
var rows=t1list.map(function(r){
var prev=t2map[r.cat+'||'+r.acc];
var dod = prev!=null? r.t1-prev : null;
var dc=dod==null?'':(dod>=0?'pos':'neg');
body+='<tr><td class="txt">'+r.cat+'</td><td class="txt">'+r.acc+'</td><td>'+n2(r.base)+'</td>'
+'<td>'+n2(r.t1)+'</td><td class="'+dc+'">'+(dod==null?'':n2(dod))+'</td></tr>';
return {cat:r.cat,acc:r.acc,base:r.base,t1:r.t1,dod:(prev!=null?r.t1-prev:null)};
});
if(DE_SORT.k){
var dir=DE_SORT.dir==='asc'?1:-1;
rows.sort(function(a,b){
if(DE_SORT.ty==='str') return dir*String(a[DE_SORT.k]).localeCompare(String(b[DE_SORT.k]));
var va=a[DE_SORT.k]==null?-Infinity:a[DE_SORT.k], vb=b[DE_SORT.k]==null?-Infinity:b[DE_SORT.k];
return (va<vb?-1:va>vb?1:0)*dir;
});
} else {
rows.sort(function(a,b){ return a.cat<b.cat?-1:a.cat>b.cat?1:(b.t1-a.t1); });
}
var body=rows.map(function(r){
var dc=r.dod==null?'':(r.dod>=0?'pos':'neg');
return '<tr><td class="txt">'+r.cat+'</td><td class="txt">'+r.acc+'</td><td>'+n2(r.base)+'</td>'
+'<td>'+n2(r.t1)+'</td><td class="'+dc+'">'+(r.dod==null?'':n2(r.dod))+'</td></tr>';
}).join('');
document.querySelector('#tCustDetail tbody').innerHTML=body;
document.querySelectorAll('#tCustDetail th.de-sort').forEach(function(th){
th.classList.remove('asc','desc');
if(th.getAttribute('data-k')===DE_SORT.k) th.classList.add(DE_SORT.dir);
});
}
// 3.1/3.2 表头排序绑定
function bindCustSort(){
document.querySelectorAll('#tCustDaily th.cd-sort').forEach(function(th){
th.style.cursor='pointer';
th.onclick=function(){
var k=th.getAttribute('data-k'), ty=th.getAttribute('data-ty');
if(CD_SORT.k===k){ CD_SORT.dir=(CD_SORT.dir==='asc'?'desc':'asc'); } else { CD_SORT.k=k; CD_SORT.ty=ty; CD_SORT.dir='desc'; }
renderCustDaily();
};
});
document.querySelectorAll('#tCustDetail th.de-sort').forEach(function(th){
th.style.cursor='pointer';
th.onclick=function(){
var k=th.getAttribute('data-k'), ty=th.getAttribute('data-ty');
if(DE_SORT.k===k){ DE_SORT.dir=(DE_SORT.dir==='asc'?'desc':'asc'); } else { DE_SORT.k=k; DE_SORT.ty=ty; DE_SORT.dir='desc'; }
renderCustDetail();
};
});
}
// 波动原因输入监听(事件委托)
@@ -249,8 +438,10 @@ fetch('data.json?_='+Date.now()).then(function(r){return r.json();}).then(functi
renderOverall();
renderAccTable('tCN', d.cn, 'DSP CN revenue');
renderAccTable('tIntl', d.international, 'DSP international');
renderPkg('tPkgMidas', d.pkg_midas, 'midas');
renderPkg('tPkgMS', d.pkg_ms, 'ms');
bindPkgControls();
bindCustSort();
renderPkg('midas');
renderPkg('ms');
renderCustDaily();
renderCustDetail();
}).catch(function(e){
+11 -8
View File
@@ -3,7 +3,7 @@
# 每小时被平台执行;UTC+8 过11点且当天未更新则补跑,查询 T-1(前一天)。
# 产出 data.json:
# overall/cn/international : 每天一行,持续叠加
# pkg_midas / pkg_ms : 每个pkg按天的revenue,保留14天(前端算DoD)
# pkg_midas / pkg_ms : {dt:{pkg:{account:rev}}} 保留14天(前端按模式聚合/算DoD)
# cust_daily / cust_detail : 客户分类,保留14天
# ============================================================
set -euo pipefail
@@ -180,17 +180,18 @@ print(f' superset T-1行={len(sup_prev)} (全部{len(sup)}) ; report行={len(re
# ---- 汇总:无_API account revenue (来自Superset attr_platform) ----
sup_acc_rev=defaultdict(float) # account(小写) -> rev
sup_pkg_rev=defaultdict(float) # pkg -> rev (Midas)
sup_pkg_acc=defaultdict(lambda:defaultdict(float)) # pkg -> {account -> rev} (Midas, account=attr_platform)
for r in sup_prev:
acc=norm(r.get('attr_platform'))
rev=float(r.get('revenue(还原前)') or 0)
sup_acc_rev[acc]+=rev
pkg=r.get('pkg name(EN)') or ''
sup_pkg_rev[pkg]+=rev
if rev:
sup_pkg_acc[pkg][r.get('attr_platform') or acc]+=rev
# ---- 汇总:带_API account revenue (来自report, 按account_id映射, 排除18) ----
rep_acc_rev=defaultdict(float) # _API名(小写) -> rev
rep_pkg_rev=defaultdict(float) # pkg -> rev (MS, 排除account18)
rep_pkg_acc=defaultdict(lambda:defaultdict(float)) # pkg -> {account -> rev} (MS, 排除account18)
for r in rep:
aid=r.get('advertiser_account_id')
rev=float(r.get('revenues') or 0)
@@ -200,7 +201,9 @@ for r in rep:
if name:
rep_acc_rev[norm(name)]+=rev
pkg=r.get('package_name') or ''
rep_pkg_rev[pkg]+=rev
disp = name if name else ('acct_%s'%aid)
if rev:
rep_pkg_acc[pkg][disp]+=rev
# 统一取某 account 收入:无_API走superset,带_API走report
def acc_rev(acc):
@@ -225,9 +228,9 @@ intl_row['DSP international']=round(intl_total,2)
overall_row={'dt':PREV_DATE,'DSP total revenue':round(cn_total+intl_total,2),
'DSP CN revenue':round(cn_total,2),'DSP international':round(intl_total,2)}
# ============ 2.1 PKG-Midas / 2.2 PKG-MS (当天pkg->rev) ============
pkg_midas={pkg:round(v,4) for pkg,v in sup_pkg_rev.items() if v} # Midas: superset
pkg_ms={pkg:round(v,4) for pkg,v in rep_pkg_rev.items() if v} # MS: report(已排除18)
# ============ 2.1 PKG-Midas / 2.2 PKG-MS (当天 pkg -> {account: rev}) ============
pkg_midas={pkg:{a:round(v,4) for a,v in accs.items() if v} for pkg,accs in sup_pkg_acc.items() if accs}
pkg_ms={pkg:{a:round(v,4) for a,v in accs.items() if v} for pkg,accs in rep_pkg_acc.items() if accs}
# ============ 3.x 客户分类 (当天) ============
# 每个 account 的 T-1 收入