Initial: EG Nagwa daily report

This commit is contained in:
caoxiaoqi
2026-08-24 18:56:38 +08:00
commit b8d174d922
4 changed files with 164 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
{
"meta": {
"geo": "EG",
"product": "Nagwa",
"price": 0.30,
"aug_budget": 7500.00,
"spend": 2843.70,
"left": 4656.30,
"spend_pct": "37.92%",
"rec_daily_install": 1940.13
},
"daily": [
{"date": "2026-08-01", "install": 338, "payable_spend": 101.40, "enroll_session": 430, "enroll_session_pct": 127.22},
{"date": "2026-08-02", "install": 384, "payable_spend": 115.20, "enroll_session": 629, "enroll_session_pct": 163.80},
{"date": "2026-08-03", "install": 370, "payable_spend": 111.00, "enroll_session": 457, "enroll_session_pct": 123.51},
{"date": "2026-08-04", "install": 385, "payable_spend": 115.50, "enroll_session": 546, "enroll_session_pct": 141.82},
{"date": "2026-08-05", "install": 397, "payable_spend": 119.10, "enroll_session": 553, "enroll_session_pct": 139.29},
{"date": "2026-08-06", "install": 366, "payable_spend": 109.80, "enroll_session": 453, "enroll_session_pct": 123.77},
{"date": "2026-08-07", "install": 392, "payable_spend": 117.60, "enroll_session": 512, "enroll_session_pct": 130.61},
{"date": "2026-08-08", "install": 412, "payable_spend": 123.60, "enroll_session": 543, "enroll_session_pct": 131.80},
{"date": "2026-08-09", "install": 405, "payable_spend": 121.50, "enroll_session": 542, "enroll_session_pct": 133.83},
{"date": "2026-08-10", "install": 368, "payable_spend": 110.40, "enroll_session": 549, "enroll_session_pct": 149.18},
{"date": "2026-08-11", "install": 386, "payable_spend": 115.80, "enroll_session": 613, "enroll_session_pct": 158.81},
{"date": "2026-08-12", "install": 354, "payable_spend": 106.20, "enroll_session": 554, "enroll_session_pct": 156.50},
{"date": "2026-08-13", "install": 377, "payable_spend": 113.10, "enroll_session": 448, "enroll_session_pct": 118.83},
{"date": "2026-08-14", "install": 368, "payable_spend": 110.40, "enroll_session": 413, "enroll_session_pct": 112.23},
{"date": "2026-08-15", "install": 376, "payable_spend": 112.80, "enroll_session": 592, "enroll_session_pct": 157.45},
{"date": "2026-08-16", "install": 446, "payable_spend": 133.80, "enroll_session": 592, "enroll_session_pct": 132.74},
{"date": "2026-08-17", "install": 257, "payable_spend": 77.10, "enroll_session": 563, "enroll_session_pct": 219.07},
{"date": "2026-08-18", "install": 245, "payable_spend": 73.50, "enroll_session": 532, "enroll_session_pct": 217.14},
{"date": "2026-08-19", "install": 256, "payable_spend": 76.80, "enroll_session": 630, "enroll_session_pct": 246.09},
{"date": "2026-08-20", "install": 638, "payable_spend": 191.40, "enroll_session": 434, "enroll_session_pct": 68.03},
{"date": "2026-08-21", "install": 654, "payable_spend": 196.20, "enroll_session": 533, "enroll_session_pct": 81.50},
{"date": "2026-08-22", "install": 636, "payable_spend": 190.80, "enroll_session": 507, "enroll_session_pct": 79.72},
{"date": "2026-08-23", "install": 669, "payable_spend": 200.70, "enroll_session": 504, "enroll_session_pct": 75.34}
]
}
+120
View File
@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EG Nagwa Daily Report - August 2026</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #333; padding: 24px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { font-size: 22px; color: #1a1a2e; margin-bottom: 4px; }
.subtitle { color: #666; font-size: 13px; margin-bottom: 24px; }
.summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 28px; }
.card { background: #fff; border-radius: 10px; padding: 16px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); }
.card .label { font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.5px; }
.card .value { font-size: 20px; font-weight: 700; margin-top: 4px; }
.card .value.blue { color: #3b82f6; }
.card .value.green { color: #10b981; }
.card .value.orange { color: #f59e0b; }
.card .value.purple { color: #8b5cf6; }
.charts { display: grid; grid-template-columns: 1fr; gap: 20px; margin-bottom: 28px; }
@media(min-width:900px) { .charts { grid-template-columns: 1fr 1fr; } }
.chart-box { background: #fff; border-radius: 10px; padding: 18px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); }
.chart-box.full { grid-column: 1 / -1; }
.chart-box h3 { font-size: 14px; color: #555; margin-bottom: 10px; }
.table-wrap { background: #fff; border-radius: 10px; padding: 18px; box-shadow: 0 1px 6px rgba(0,0,0,0.06); overflow-x: auto; }
.table-wrap h3 { font-size: 14px; color: #555; margin-bottom: 10px; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
th, td { padding: 8px 10px; text-align: right; border-bottom: 1px solid #f0f0f0; }
th { background: #f8fafc; color: #666; font-weight: 600; position: sticky; top: 0; }
td:first-child, th:first-child { text-align: left; }
tr:hover td { background: #f5f9ff; }
.footer { text-align: center; color: #aaa; font-size: 11px; margin-top: 20px; }
</style>
</head>
<body>
<div class="container">
<h1>EG Nagwa Daily Report</h1>
<p class="subtitle">Egypt &middot; August 2026 &middot; Install / Enroll Session / Revenue</p>
<div class="summary-cards" id="summary"></div>
<div class="charts">
<div class="chart-box full">
<h3>Daily Install vs Enroll Session</h3>
<canvas id="chartMain" height="70"></canvas>
</div>
<div class="chart-box">
<h3>Enroll Session Rate (%)</h3>
<canvas id="chartRate" height="90"></canvas>
</div>
<div class="chart-box">
<h3>Daily Spend ($)</h3>
<canvas id="chartSpend" height="90"></canvas>
</div>
</div>
<div class="table-wrap">
<h3>Daily Detail</h3>
<table>
<thead><tr><th>Date</th><th>Install</th><th>Spend</th><th>Enroll Session</th><th>Enroll %</th></tr></thead>
<tbody id="tbody"></tbody>
</table>
</div>
<p class="footer" id="foot"></p>
</div>
<script>
fetch('data.json').then(r=>r.json()).then(({meta, daily})=>{
const valid = daily.filter(d=>d.install>0);
const totalInstall = valid.reduce((s,d)=>s+d.install,0);
const totalSpend = valid.reduce((s,d)=>s+d.payable_spend,0);
const totalEnroll = valid.reduce((s,d)=>s+d.enroll_session,0);
const avgRate = (totalEnroll/totalInstall*100).toFixed(1);
document.getElementById('summary').innerHTML = `
<div class="card"><div class="label">Total Install (MTD)</div><div class="value blue">${totalInstall.toLocaleString()}</div></div>
<div class="card"><div class="label">Total Enroll Session</div><div class="value green">${totalEnroll.toLocaleString()}</div></div>
<div class="card"><div class="label">Avg Enroll Rate</div><div class="value orange">${avgRate}%</div></div>
<div class="card"><div class="label">Total Spend</div><div class="value purple">$${totalSpend.toFixed(2)}</div></div>
<div class="card"><div class="label">Budget</div><div class="value">$${meta.aug_budget.toLocaleString()}</div></div>
<div class="card"><div class="label">Budget Left</div><div class="value green">$${meta.left.toLocaleString()}</div></div>
<div class="card"><div class="label">Budget Used</div><div class="value orange">${meta.spend_pct}</div></div>
<div class="card"><div class="label">Rec. Daily Install</div><div class="value blue">${meta.rec_daily_install.toLocaleString()}</div></div>
`;
const labels = valid.map(d=>d.date.slice(5));
const installs = valid.map(d=>d.install);
const enrolls = valid.map(d=>d.enroll_session);
const rates = valid.map(d=>d.enroll_session_pct);
const spends = valid.map(d=>d.payable_spend);
new Chart(document.getElementById('chartMain'),{
type:'line',
data:{labels, datasets:[
{label:'Install', data:installs, borderColor:'#3b82f6', backgroundColor:'rgba(59,130,246,0.07)', tension:0.3, fill:true, pointRadius:3},
{label:'Enroll Session', data:enrolls, borderColor:'#10b981', backgroundColor:'rgba(16,185,129,0.07)', tension:0.3, fill:true, pointRadius:3}
]},
options:{responsive:true, plugins:{legend:{position:'top'}}, scales:{y:{beginAtZero:false}}}
});
new Chart(document.getElementById('chartRate'),{
type:'bar',
data:{labels, datasets:[{label:'Enroll %', data:rates, backgroundColor:'rgba(245,158,11,0.55)', borderColor:'#f59e0b', borderWidth:1}]},
options:{responsive:true, plugins:{legend:{display:false}}, scales:{y:{ticks:{callback:v=>v+'%'}}}}
});
new Chart(document.getElementById('chartSpend'),{
type:'line',
data:{labels, datasets:[{label:'Spend', data:spends, borderColor:'#8b5cf6', backgroundColor:'rgba(139,92,246,0.08)', tension:0.3, fill:true, pointRadius:3}]},
options:{responsive:true, plugins:{legend:{display:false}}, scales:{y:{ticks:{callback:v=>'$'+v}}}}
});
const tbody = document.getElementById('tbody');
valid.slice().reverse().forEach(d=>{
tbody.innerHTML+=`<tr><td>${d.date}</td><td>${d.install}</td><td>$${d.payable_spend.toFixed(2)}</td><td>${d.enroll_session}</td><td>${d.enroll_session_pct.toFixed(2)}%</td></tr>`;
});
document.getElementById('foot').textContent='Last updated: '+new Date().toLocaleString();
});
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
24
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# update.sh - 数据来自手动更新的 data.json
# 由于数据源是 Excel 表格(无公开 API),此脚本仅做占位
# 每次有新数据时,直接更新 data.json 并 push 即可
echo "data.json is manually maintained from Excel export."
echo "Last check: $(date)"