Add August delivery and budget pacing

This commit is contained in:
zouyun
2026-08-24 16:30:06 +08:00
parent 0042f10495
commit 7ba8ff6936
5 changed files with 8881 additions and 94 deletions
+535 -13
View File
@@ -2,9 +2,10 @@
"meta": { "meta": {
"package": "com.amazon.mShop.android.shopping", "package": "com.amazon.mShop.android.shopping",
"geo": "ZA", "geo": "ZA",
"generated_at": "2026-08-24T07:48:40+00:00", "generated_at": "2026-08-24T08:27:15+00:00",
"date_start": "2026-08-21", "date_start": "2026-08-01",
"date_end": "2026-08-23", "date_end": "2026-08-31",
"data_cutoff": "2026-08-23",
"currency": "USD", "currency": "USD",
"install_definition": "af_app_opened repostback", "install_definition": "af_app_opened repostback",
"event_definition": "event1/event2 repostback", "event_definition": "event1/event2 repostback",
@@ -12,44 +13,565 @@
"payable_definition": "(event1 + event2) × $3" "payable_definition": "(event1 + event2) × $3"
}, },
"totals": { "totals": {
"install": 5636, "install": 39149,
"event1": 1029, "event1": 6208,
"event2": 27, "event2": 485,
"e1e2": 1056, "e1e2": 6693,
"midas_spend": 2920.17, "midas_spend": 19461.57,
"payable_spend": 3168, "midas_spend_zar": 321115.9,
"e1e2_rate": 18.74 "payable_spend": 20079,
"payable_spend_zar": 331303.5,
"e1e2_rate": 17.1
},
"budget": {
"monthly_budget_zar": 511500.0,
"monthly_budget_usd": 31000.0,
"daily_budget_zar": 16500.0,
"daily_budget_usd": 1000.0,
"unit_price_zar": 49.5,
"unit_price_usd": 3.0,
"fx_zar_per_usd": 16.5,
"daily_cap": 333,
"used_budget_zar": 331303.5,
"used_rate": 64.77,
"remaining_budget_zar": 180196.5,
"remaining_budget_usd": 10921.0,
"expected_budget_to_date_zar": 379500.0,
"pacing_rate": 87.3,
"elapsed_days": 23,
"remaining_days": 8,
"required_daily_budget_zar": 22524.56,
"required_daily_events": 455,
"projected_month_spend_zar": 446539.5,
"projected_month_variance_zar": 64960.5
}, },
"daily": [ "daily": [
{
"date": "2026-08-01",
"is_actual": true,
"install": 1520,
"event1": 281,
"event2": 23,
"e1e2": 304,
"e1e2_rate": 20.0,
"midas_spend": 920.34,
"midas_spend_zar": 15185.61,
"payable_spend": 912,
"payable_spend_zar": 15048.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 91.2,
"ecpi_usd": 0.6055
},
{
"date": "2026-08-02",
"is_actual": true,
"install": 1733,
"event1": 94,
"event2": 26,
"e1e2": 120,
"e1e2_rate": 6.92,
"midas_spend": 1036.81,
"midas_spend_zar": 17107.36,
"payable_spend": 360,
"payable_spend_zar": 5940.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 36.0,
"ecpi_usd": 0.5983
},
{
"date": "2026-08-03",
"is_actual": true,
"install": 1781,
"event1": 432,
"event2": 26,
"e1e2": 458,
"e1e2_rate": 25.72,
"midas_spend": 963.34,
"midas_spend_zar": 15895.11,
"payable_spend": 1374,
"payable_spend_zar": 22671.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 137.4,
"ecpi_usd": 0.5409
},
{
"date": "2026-08-04",
"is_actual": true,
"install": 1661,
"event1": 170,
"event2": 36,
"e1e2": 206,
"e1e2_rate": 12.4,
"midas_spend": 686.46,
"midas_spend_zar": 11326.59,
"payable_spend": 618,
"payable_spend_zar": 10197.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 61.8,
"ecpi_usd": 0.4133
},
{
"date": "2026-08-05",
"is_actual": true,
"install": 1622,
"event1": 141,
"event2": 36,
"e1e2": 177,
"e1e2_rate": 10.91,
"midas_spend": 651.98,
"midas_spend_zar": 10757.67,
"payable_spend": 531,
"payable_spend_zar": 8761.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 53.1,
"ecpi_usd": 0.402
},
{
"date": "2026-08-06",
"is_actual": true,
"install": 1397,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": 0.0,
"midas_spend": 597.23,
"midas_spend_zar": 9854.3,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": 0.4275
},
{
"date": "2026-08-07",
"is_actual": true,
"install": 1870,
"event1": 516,
"event2": 36,
"e1e2": 552,
"e1e2_rate": 29.52,
"midas_spend": 637.97,
"midas_spend_zar": 10526.51,
"payable_spend": 1656,
"payable_spend_zar": 27324.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 165.6,
"ecpi_usd": 0.3412
},
{
"date": "2026-08-08",
"is_actual": true,
"install": 1506,
"event1": 196,
"event2": 27,
"e1e2": 223,
"e1e2_rate": 14.81,
"midas_spend": 603.36,
"midas_spend_zar": 9955.44,
"payable_spend": 669,
"payable_spend_zar": 11038.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 66.9,
"ecpi_usd": 0.4006
},
{
"date": "2026-08-09",
"is_actual": true,
"install": 1633,
"event1": 325,
"event2": 6,
"e1e2": 331,
"e1e2_rate": 20.27,
"midas_spend": 659.2,
"midas_spend_zar": 10876.8,
"payable_spend": 993,
"payable_spend_zar": 16384.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 99.3,
"ecpi_usd": 0.4037
},
{
"date": "2026-08-10",
"is_actual": true,
"install": 1661,
"event1": 242,
"event2": 20,
"e1e2": 262,
"e1e2_rate": 15.77,
"midas_spend": 727.32,
"midas_spend_zar": 12000.78,
"payable_spend": 786,
"payable_spend_zar": 12969.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 78.6,
"ecpi_usd": 0.4379
},
{
"date": "2026-08-11",
"is_actual": true,
"install": 1668,
"event1": 227,
"event2": 16,
"e1e2": 243,
"e1e2_rate": 14.57,
"midas_spend": 871.5,
"midas_spend_zar": 14379.75,
"payable_spend": 729,
"payable_spend_zar": 12028.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 72.9,
"ecpi_usd": 0.5225
},
{
"date": "2026-08-12",
"is_actual": true,
"install": 1650,
"event1": 266,
"event2": 22,
"e1e2": 288,
"e1e2_rate": 17.45,
"midas_spend": 881.7,
"midas_spend_zar": 14548.05,
"payable_spend": 864,
"payable_spend_zar": 14256.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 86.4,
"ecpi_usd": 0.5344
},
{
"date": "2026-08-13",
"is_actual": true,
"install": 1543,
"event1": 41,
"event2": 5,
"e1e2": 46,
"e1e2_rate": 2.98,
"midas_spend": 827.89,
"midas_spend_zar": 13660.18,
"payable_spend": 138,
"payable_spend_zar": 2277.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 13.8,
"ecpi_usd": 0.5365
},
{
"date": "2026-08-14",
"is_actual": true,
"install": 1677,
"event1": 176,
"event2": 23,
"e1e2": 199,
"e1e2_rate": 11.87,
"midas_spend": 895.51,
"midas_spend_zar": 14775.91,
"payable_spend": 597,
"payable_spend_zar": 9850.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 59.7,
"ecpi_usd": 0.534
},
{
"date": "2026-08-15",
"is_actual": true,
"install": 1839,
"event1": 373,
"event2": 15,
"e1e2": 388,
"e1e2_rate": 21.1,
"midas_spend": 983.11,
"midas_spend_zar": 16221.32,
"payable_spend": 1164,
"payable_spend_zar": 19206.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 116.4,
"ecpi_usd": 0.5346
},
{
"date": "2026-08-16",
"is_actual": true,
"install": 1809,
"event1": 273,
"event2": 72,
"e1e2": 345,
"e1e2_rate": 19.07,
"midas_spend": 971.76,
"midas_spend_zar": 16034.04,
"payable_spend": 1035,
"payable_spend_zar": 17077.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 103.5,
"ecpi_usd": 0.5372
},
{
"date": "2026-08-17",
"is_actual": true,
"install": 1675,
"event1": 266,
"event2": 8,
"e1e2": 274,
"e1e2_rate": 16.36,
"midas_spend": 908.82,
"midas_spend_zar": 14995.53,
"payable_spend": 822,
"payable_spend_zar": 13563.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 82.2,
"ecpi_usd": 0.5426
},
{
"date": "2026-08-18",
"is_actual": true,
"install": 1988,
"event1": 370,
"event2": 40,
"e1e2": 410,
"e1e2_rate": 20.62,
"midas_spend": 993.6,
"midas_spend_zar": 16394.4,
"payable_spend": 1230,
"payable_spend_zar": 20295.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 123.0,
"ecpi_usd": 0.4998
},
{
"date": "2026-08-19",
"is_actual": true,
"install": 1543,
"event1": 438,
"event2": 13,
"e1e2": 451,
"e1e2_rate": 29.23,
"midas_spend": 812.35,
"midas_spend_zar": 13403.77,
"payable_spend": 1353,
"payable_spend_zar": 22324.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 135.3,
"ecpi_usd": 0.5265
},
{
"date": "2026-08-20",
"is_actual": true,
"install": 1737,
"event1": 352,
"event2": 8,
"e1e2": 360,
"e1e2_rate": 20.73,
"midas_spend": 911.15,
"midas_spend_zar": 15033.98,
"payable_spend": 1080,
"payable_spend_zar": 17820.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 108.0,
"ecpi_usd": 0.5246
},
{ {
"date": "2026-08-21", "date": "2026-08-21",
"is_actual": true,
"install": 1877, "install": 1877,
"event1": 265, "event1": 265,
"event2": 4, "event2": 4,
"e1e2": 269, "e1e2": 269,
"e1e2_rate": 14.33, "e1e2_rate": 14.33,
"midas_spend": 989.64, "midas_spend": 989.64,
"payable_spend": 807 "midas_spend_zar": 16329.06,
"payable_spend": 807,
"payable_spend_zar": 13315.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 80.7,
"ecpi_usd": 0.5272
}, },
{ {
"date": "2026-08-22", "date": "2026-08-22",
"is_actual": true,
"install": 1883, "install": 1883,
"event1": 435, "event1": 435,
"event2": 13, "event2": 13,
"e1e2": 448, "e1e2": 448,
"e1e2_rate": 23.79, "e1e2_rate": 23.79,
"midas_spend": 942.89, "midas_spend": 942.89,
"payable_spend": 1344 "midas_spend_zar": 15557.68,
"payable_spend": 1344,
"payable_spend_zar": 22176.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 134.4,
"ecpi_usd": 0.5007
}, },
{ {
"date": "2026-08-23", "date": "2026-08-23",
"is_actual": true,
"install": 1876, "install": 1876,
"event1": 329, "event1": 329,
"event2": 10, "event2": 10,
"e1e2": 339, "e1e2": 339,
"e1e2_rate": 18.07, "e1e2_rate": 18.07,
"midas_spend": 987.64, "midas_spend": 987.64,
"payable_spend": 1017 "midas_spend_zar": 16296.06,
"payable_spend": 1017,
"payable_spend_zar": 16780.5,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 101.7,
"ecpi_usd": 0.5265
},
{
"date": "2026-08-24",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-25",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-26",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-27",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-28",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-29",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-30",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
},
{
"date": "2026-08-31",
"is_actual": false,
"install": 0,
"event1": 0,
"event2": 0,
"e1e2": 0,
"e1e2_rate": null,
"midas_spend": 0.0,
"midas_spend_zar": 0.0,
"payable_spend": 0,
"payable_spend_zar": 0.0,
"daily_budget_zar": 16500.0,
"daily_cap": 333,
"ffr_rate": 0.0,
"ecpi_usd": null
} }
] ]
} }
+12 -8
View File
@@ -7,7 +7,7 @@
<title>Amazon ZA 每日交付报告</title> <title>Amazon ZA 每日交付报告</title>
<style> <style>
:root{--bg:#f4f7fb;--panel:#fff;--ink:#162033;--muted:#667085;--line:#dce3ec;--blue:#2f6fed;--gold:#d79b24;--orange:#e56d35;--soft:#eaf1ff;--shadow:0 8px 28px rgba(23,43,77,.08)} :root{--bg:#f4f7fb;--panel:#fff;--ink:#162033;--muted:#667085;--line:#dce3ec;--blue:#2f6fed;--gold:#d79b24;--orange:#e56d35;--soft:#eaf1ff;--shadow:0 8px 28px rgba(23,43,77,.08)}
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.55 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}main{max-width:1180px;margin:auto;padding:42px 24px 64px}.eyebrow{color:var(--blue);font-weight:700;letter-spacing:.08em;text-transform:uppercase}.hero h1{font-size:clamp(28px,4vw,46px);line-height:1.1;margin:8px 0 12px}.hero p{color:var(--muted);font-size:16px;margin:0;max-width:800px}.status{display:inline-flex;align-items:center;gap:8px;margin-top:18px;padding:7px 11px;border:1px solid var(--line);border-radius:999px;background:var(--panel);color:var(--muted)}.dot{width:8px;height:8px;border-radius:50%;background:var(--blue)}section{margin-top:30px}h2{font-size:22px;margin:0 0 12px}.summary{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}.card,.panel{background:var(--panel);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow)}.card{padding:18px}.card .label{color:var(--muted);font-weight:600}.card .value{font-size:28px;font-weight:750;margin-top:6px;font-variant-numeric:tabular-nums}.card .note{color:var(--muted);font-size:12px;margin-top:3px}.panel{padding:22px;margin-top:14px}.takeaway{border-left:4px solid var(--blue);padding-left:14px;color:var(--muted)}.takeaway strong{color:var(--ink)}.charts{display:grid;grid-template-columns:1fr 1fr;gap:14px}.chart{min-height:300px}.chart-head{margin-bottom:16px}.chart-title{font-weight:750;font-size:16px}.chart-sub{color:var(--muted);font-size:12px}.bars{height:220px;display:flex;align-items:end;gap:14px;padding:20px 10px 28px;border-bottom:1px solid var(--line);position:relative}.bar-group{flex:1;height:100%;display:flex;align-items:end;justify-content:center;gap:6px;position:relative}.bar{min-width:16px;max-width:34px;width:33%;border-radius:6px 6px 0 0;position:relative}.bar span{position:absolute;top:-21px;left:50%;transform:translateX(-50%);font-size:11px;font-weight:700;white-space:nowrap}.bar-label{position:absolute;bottom:-25px;left:50%;transform:translateX(-50%);color:var(--muted);font-size:11px}.legend{display:flex;gap:14px;margin-top:13px;color:var(--muted);font-size:12px}.swatch{width:9px;height:9px;border-radius:2px;display:inline-block;margin-right:5px}.rate-bars .bar{width:52%;background:var(--orange)}table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums}th,td{padding:12px 10px;border-bottom:1px solid var(--line);text-align:right;white-space:nowrap}th:first-child,td:first-child{text-align:left}th{color:var(--muted);font-size:12px}tbody tr:hover{background:var(--soft)}.footer-note{color:var(--muted);font-size:12px;margin-top:18px}.error{padding:18px;border:1px solid #f0b7b7;background:#fff2f2;color:#912b2b;border-radius:12px}.login-shell{min-height:100vh;display:grid;place-items:center;padding:24px}.login-card{width:min(430px,100%);background:var(--panel);border:1px solid var(--line);border-radius:22px;padding:30px;box-shadow:var(--shadow)}.login-card h1{font-size:28px;margin:8px 0}.login-card p{color:var(--muted);margin:0 0 22px}.field{display:block;margin-top:14px;font-weight:700}.field input{display:block;width:100%;margin-top:7px;border:1px solid var(--line);border-radius:11px;padding:12px 13px;background:var(--bg);color:var(--ink);font:inherit;outline:none}.field input:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(47,111,237,.14)}button{width:100%;margin-top:20px;border:0;border-radius:11px;padding:13px;background:var(--blue);color:white;font:inherit;font-weight:750;cursor:pointer}button:disabled{opacity:.65;cursor:wait}.login-error{min-height:22px;color:#c13a3a;font-size:13px;margin-top:10px}.access-note{font-size:12px!important;margin-top:18px!important}#report[hidden],#login[hidden]{display:none!important}@media(max-width:850px){.summary{grid-template-columns:1fr 1fr}.charts{grid-template-columns:1fr}}@media(max-width:520px){main{padding:26px 14px}.summary{grid-template-columns:1fr}.panel{padding:16px;overflow:auto}.card .value{font-size:24px}.login-card{padding:24px}} *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.55 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}main{max-width:1180px;margin:auto;padding:42px 24px 64px}.eyebrow{color:var(--blue);font-weight:700;letter-spacing:.08em;text-transform:uppercase}.hero h1{font-size:clamp(28px,4vw,46px);line-height:1.1;margin:8px 0 12px}.hero p{color:var(--muted);font-size:16px;margin:0;max-width:800px}.status{display:inline-flex;align-items:center;gap:8px;margin-top:18px;padding:7px 11px;border:1px solid var(--line);border-radius:999px;background:var(--panel);color:var(--muted)}.dot{width:8px;height:8px;border-radius:50%;background:var(--blue)}section{margin-top:30px}h2{font-size:22px;margin:0 0 12px}.summary{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}.card,.panel{background:var(--panel);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow)}.card{padding:18px}.card .label{color:var(--muted);font-weight:600}.card .value{font-size:28px;font-weight:750;margin-top:6px;font-variant-numeric:tabular-nums}.card .note{color:var(--muted);font-size:12px;margin-top:3px}.panel{padding:22px;margin-top:14px}.takeaway{border-left:4px solid var(--blue);padding-left:14px;color:var(--muted)}.takeaway strong{color:var(--ink)}.charts{display:grid;grid-template-columns:1fr 1fr;gap:14px}.chart{min-height:300px;overflow-x:auto}.chart-head{margin-bottom:16px}.chart-title{font-weight:750;font-size:16px}.chart-sub{color:var(--muted);font-size:12px}.bars{height:220px;display:flex;align-items:end;gap:14px;padding:20px 10px 28px;border-bottom:1px solid var(--line);position:relative}.bar-group{flex:1;height:100%;display:flex;align-items:end;justify-content:center;gap:6px;position:relative}.bar{min-width:8px;max-width:34px;width:33%;border-radius:6px 6px 0 0;position:relative}.bar span{position:absolute;top:-21px;left:50%;transform:translateX(-50%);font-size:11px;font-weight:700;white-space:nowrap}.bar-label{position:absolute;bottom:-25px;left:50%;transform:translateX(-50%);color:var(--muted);font-size:11px}.legend{display:flex;gap:14px;margin-top:13px;color:var(--muted);font-size:12px}.swatch{width:9px;height:9px;border-radius:2px;display:inline-block;margin-right:5px}.rate-bars .bar{width:52%;background:var(--orange)}.progress{height:14px;background:var(--soft);border-radius:999px;overflow:hidden;margin:18px 0 8px}.progress-fill{height:100%;background:var(--blue);border-radius:inherit}.progress-labels{display:flex;justify-content:space-between;color:var(--muted);font-size:12px}table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums}th,td{padding:11px 9px;border-bottom:1px solid var(--line);text-align:right;white-space:nowrap}th:first-child,td:first-child{text-align:left}th{color:var(--muted);font-size:12px}tbody tr:hover{background:var(--soft)}tr.future{color:var(--muted);opacity:.65}.footer-note{color:var(--muted);font-size:12px;margin-top:18px}.error{padding:18px;border:1px solid #f0b7b7;background:#fff2f2;color:#912b2b;border-radius:12px}.login-shell{min-height:100vh;display:grid;place-items:center;padding:24px}.login-card{width:min(430px,100%);background:var(--panel);border:1px solid var(--line);border-radius:22px;padding:30px;box-shadow:var(--shadow)}.login-card h1{font-size:28px;margin:8px 0}.login-card p{color:var(--muted);margin:0 0 22px}.field{display:block;margin-top:14px;font-weight:700}.field input{display:block;width:100%;margin-top:7px;border:1px solid var(--line);border-radius:11px;padding:12px 13px;background:var(--bg);color:var(--ink);font:inherit;outline:none}.field input:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(47,111,237,.14)}button{width:100%;margin-top:20px;border:0;border-radius:11px;padding:13px;background:var(--blue);color:white;font:inherit;font-weight:750;cursor:pointer}button:disabled{opacity:.65;cursor:wait}.login-error{min-height:22px;color:#c13a3a;font-size:13px;margin-top:10px}.access-note{font-size:12px!important;margin-top:18px!important}#report[hidden],#login[hidden]{display:none!important}@media(max-width:850px){.summary{grid-template-columns:1fr 1fr}.charts{grid-template-columns:1fr}}@media(max-width:520px){main{padding:26px 14px}.summary{grid-template-columns:1fr}.panel{padding:16px;overflow:auto}.card .value{font-size:24px}.login-card{padding:24px}}
@media(prefers-color-scheme:dark){:root{--bg:#0c1220;--panel:#121a2a;--ink:#eef3fb;--muted:#9ba9bd;--line:#28354a;--soft:#182743;--shadow:none}} @media(prefers-color-scheme:dark){:root{--bg:#0c1220;--panel:#121a2a;--ink:#eef3fb;--muted:#9ba9bd;--line:#28354a;--soft:#182743;--shadow:none}}
</style> </style>
</head> </head>
@@ -15,26 +15,30 @@
<header class="hero"><div class="eyebrow">Daily Delivery · ZA</div><h1>Amazon ZA 每日交付报告</h1><p>包名:com.amazon.mShop.android.shopping。聚焦 repostback 交付、E1+E2 转化与 Midas / Payable spend。</p><div class="status"><span class="dot"></span><span id="range">正在载入数据…</span></div></header> <header class="hero"><div class="eyebrow">Daily Delivery · ZA</div><h1>Amazon ZA 每日交付报告</h1><p>包名:com.amazon.mShop.android.shopping。聚焦 repostback 交付、E1+E2 转化与 Midas / Payable spend。</p><div class="status"><span class="dot"></span><span id="range">正在载入数据…</span></div></header>
<section><h2>Executive Summary</h2><div id="summaryText" class="panel takeaway">载入中…</div></section> <section><h2>Executive Summary</h2><div id="summaryText" class="panel takeaway">载入中…</div></section>
<section><h2>核心指标</h2><div class="summary" id="cards"></div></section> <section><h2>核心指标</h2><div class="summary" id="cards"></div></section>
<section><h2>每日交付与转化</h2><div class="takeaway"><strong>如何阅读:</strong>先看每日 Install 与 E1/E2 的规模,再结合 E1+E2% 判断转化效率;仅有 3 个日期,因此使用离散柱状图,避免把短窗口误读为长期趋势。</div><div class="charts"><div class="panel chart"><div class="chart-head"><div class="chart-title">每日 Install、Event1 与 Event2</div><div class="chart-sub">均取 repostback;按 ZA 日期汇总</div></div><div id="deliveryChart" class="bars"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Install</span><span><i class="swatch" style="background:var(--gold)"></i>Event1</span><span><i class="swatch" style="background:var(--orange)"></i>Event2</span></div></div><div class="panel chart"><div class="chart-head"><div class="chart-title">每日 E1+E2%</div><div class="chart-sub">(Event1 + Event2) ÷ Install</div></div><div id="rateChart" class="bars rate-bars"></div></div></div></section> <section><h2>8 月预算消耗</h2><div id="budgetText" class="takeaway"></div><div class="summary" id="budgetCards" style="margin-top:14px"></div><div class="panel"><div class="chart-title">月度预算使用进度</div><div class="chart-sub">预算按 E1+E2 × 49.50 ZAR 扣减</div><div class="progress"><div id="budgetProgress" class="progress-fill"></div></div><div class="progress-labels"><span id="budgetUsed"></span><span id="budgetTotal"></span></div></div></section>
<section><h2>每日交付与转化</h2><div class="takeaway"><strong>如何阅读:</strong>柱状图展示本月截至数据日的逐日 repostback 交付;结合 E1+E2% 判断每日转化效率,整月完整日期则保留在下方明细表。</div><div class="charts"><div class="panel chart"><div class="chart-head"><div class="chart-title">每日 Install、Event1 与 Event2</div><div class="chart-sub">均取 repostback;按 ZA 日期汇总</div></div><div id="deliveryChart" class="bars"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Install</span><span><i class="swatch" style="background:var(--gold)"></i>Event1</span><span><i class="swatch" style="background:var(--orange)"></i>Event2</span></div></div><div class="panel chart"><div class="chart-head"><div class="chart-title">每日 E1+E2%</div><div class="chart-sub">(Event1 + Event2) ÷ Install</div></div><div id="rateChart" class="bars rate-bars"></div></div></div></section>
<section><h2>消耗对比</h2><div id="spendText" class="takeaway"></div><div class="panel chart"><div class="chart-head"><div class="chart-title">Midas spend 与 Payable spend</div><div class="chart-sub">USD;Payable spend = (E1 + E2) × $3</div></div><div id="spendChart" class="bars"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Midas spend</span><span><i class="swatch" style="background:var(--gold)"></i>Payable spend</span></div></div></section> <section><h2>消耗对比</h2><div id="spendText" class="takeaway"></div><div class="panel chart"><div class="chart-head"><div class="chart-title">Midas spend 与 Payable spend</div><div class="chart-sub">USD;Payable spend = (E1 + E2) × $3</div></div><div id="spendChart" class="bars"></div><div class="legend"><span><i class="swatch" style="background:var(--blue)"></i>Midas spend</span><span><i class="swatch" style="background:var(--gold)"></i>Payable spend</span></div></div></section>
<section><h2>每日明细</h2><div class="panel"><table><thead><tr><th>日期</th><th>Install</th><th>Event1</th><th>Event2</th><th>E1+E2%</th><th>Midas spend</th><th>Payable spend</th></tr></thead><tbody id="rows"></tbody></table><div class="footer-note">口径:Install、Event1、Event2 均取 repostback;金额为 USD。源文件更新后,update.sh 会重新生成 data.json。</div></div></section> <section><h2>8 月每日明细</h2><div class="panel" style="overflow-x:auto"><table><thead><tr><th>日期</th><th>Install</th><th>Event1</th><th>Event2</th><th>Total</th><th>Event rate</th><th>Payable (ZAR)</th><th>日预算</th><th>FFR</th><th>Midas (USD)</th><th>eCPI</th></tr></thead><tbody id="rows"></tbody></table><div class="footer-note">口径:Install、Event1、Event2 均取 repostback;Total = E1+E2;FFR = Total ÷ Daily cap。灰色日期为数据截止日之后的剩余月份。</div></div></section>
<section><h2>建议关注</h2><div class="panel"><ul><li>继续监控 E1+E2% 是否在更长日期窗口内稳定,当前 3 天样本不宜外推长期趋势。</li><li>对比 Payable spend 与 Midas spend 的日级差额,优先排查差额扩大日期的事件质量和计费口径。</li><li>若要实现新增日期自动进入报告,需要让 update.sh 能访问持续更新的数据源;当前版本自动重算仓库内两份 CSV 快照。</li></ul></div></section> <section><h2>建议关注</h2><div class="panel"><ul><li>优先关注 FFR 明显低于 100% 的日期,判断是流量不足、事件率下降还是日 cap 执行问题。</li><li>剩余预算需要的日均消耗已高于当前日预算;如目标是完整消耗月预算,需要确认是否提高每日 cap。</li><li>对比 Payable spend 与 Midas spend 的日级差额,排查差额扩大日期的事件质量和计费口径。</li></ul></div></section>
</main><script> </main><script>
const fmt=n=>new Intl.NumberFormat('en-US').format(n), money=n=>'$'+new Intl.NumberFormat('en-US',{minimumFractionDigits:2,maximumFractionDigits:2}).format(n), pct=n=>n==null?'—':n.toFixed(2)+'%'; const fmt=n=>new Intl.NumberFormat('en-US').format(n), money=n=>'$'+new Intl.NumberFormat('en-US',{minimumFractionDigits:2,maximumFractionDigits:2}).format(n), pct=n=>n==null?'—':n.toFixed(2)+'%';
const label=d=>d.slice(5).replace('-','/'); const label=d=>d.slice(5).replace('-','/');
function bars(el,data,series,max){el.innerHTML=data.map(d=>`<div class="bar-group">${series.map(s=>`<div class="bar" style="height:${Math.max(2,d[s.key]/max*100)}%;background:${s.color}"><span>${s.format(d[s.key])}</span></div>`).join('')}<div class="bar-label">${label(d.date)}</div></div>`).join('')} function bars(el,data,series,max){const showValues=data.length<=12;el.style.minWidth=Math.max(0,data.length*48)+'px';el.innerHTML=data.map(d=>`<div class="bar-group">${series.map(s=>`<div class="bar" style="height:${Math.max(2,d[s.key]/max*100)}%;background:${s.color}">${showValues?`<span>${s.format(d[s.key])}</span>`:''}</div>`).join('')}<div class="bar-label">${label(d.date)}</div></div>`).join('')}
const ACCESS={emails:['a91dc567bcdb6d054cf121d7e4016454ee3d316e3b452b851ae4d3087bc99bb2'],salt:'amazon-za-delivery-2026-08',password:'2344c6c9cf72430cafbf48e014d7f721a53484f766f1455f0c12232515ec61e9'}; const ACCESS={emails:['a91dc567bcdb6d054cf121d7e4016454ee3d316e3b452b851ae4d3087bc99bb2'],salt:'amazon-za-delivery-2026-08',password:'2344c6c9cf72430cafbf48e014d7f721a53484f766f1455f0c12232515ec61e9'};
async function sha256(value){const bytes=new TextEncoder().encode(value);const digest=await crypto.subtle.digest('SHA-256',bytes);return [...new Uint8Array(digest)].map(x=>x.toString(16).padStart(2,'0')).join('')} async function sha256(value){const bytes=new TextEncoder().encode(value);const digest=await crypto.subtle.digest('SHA-256',bytes);return [...new Uint8Array(digest)].map(x=>x.toString(16).padStart(2,'0')).join('')}
function showReport(){document.getElementById('login').hidden=true;document.getElementById('report').hidden=false;loadReport()} function showReport(){document.getElementById('login').hidden=true;document.getElementById('report').hidden=false;loadReport()}
async function loadReport(){fetch('data.json',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('data.json '+r.status);return r.json()}).then(data=>{ async function loadReport(){fetch('data.json',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('data.json '+r.status);return r.json()}).then(data=>{
const t=data.totals,d=data.daily,latest=d[d.length-1],first=d[0];document.getElementById('range').textContent=`${data.meta.date_start} 至 ${data.meta.date_end} · 每日快照`; const t=data.totals,b=data.budget,all=data.daily,d=all.filter(x=>x.is_actual),latest=d[d.length-1],first=d[0];document.getElementById('range').textContent=`2026-08-01 至 2026-08-31 · 数据截至 ${data.meta.data_cutoff}`;
const delta=first&&latest?latest.e1e2_rate-first.e1e2_rate:0;document.getElementById('summaryText').innerHTML=`<strong>${d.length} 天累计 ${fmt(t.install)} Install,产生 ${fmt(t.e1e2)} 个 E1+E2,整体转化率 ${pct(t.e1e2_rate)}。</strong> Midas spend 为 ${money(t.midas_spend)},按每个 E1/E2 事件 $3 计算的 Payable spend 为 ${money(t.payable_spend)}。窗口首尾 E1+E2% ${delta>=0?'上升':'下降'} ${Math.abs(delta).toFixed(2)} 个百分点;由于仅有 ${d.length} 天数据,应作为短期交付快照解读。`; const delta=first&&latest?latest.e1e2_rate-first.e1e2_rate:0;document.getElementById('summaryText').innerHTML=`<strong>截至 ${data.meta.data_cutoff},累计 ${fmt(t.install)} Install,产生 ${fmt(t.e1e2)} 个 E1+E2,整体转化率 ${pct(t.e1e2_rate)}。</strong> 已消耗月预算 ${pct(b.used_rate)},当前消耗进度相当于同期预算的 ${pct(b.pacing_rate)}。按当前日均速度推算,月底预计${b.projected_month_variance_zar>=0?'结余':'超支'} ZAR ${fmt(Math.abs(b.projected_month_variance_zar))}。`;
const cards=[['Install',fmt(t.install),'af_app_opened repostback'],['E1 + E2',fmt(t.e1e2),`${fmt(t.event1)} E1 · ${fmt(t.event2)} E2`],['E1+E2%',pct(t.e1e2_rate),'累计 E1+E2 ÷ 累计 Install'],['Payable spend',money(t.payable_spend),`Midas spend ${money(t.midas_spend)}`]];document.getElementById('cards').innerHTML=cards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join(''); const cards=[['Install',fmt(t.install),'af_app_opened repostback'],['E1 + E2',fmt(t.e1e2),`${fmt(t.event1)} E1 · ${fmt(t.event2)} E2`],['E1+E2%',pct(t.e1e2_rate),'累计 E1+E2 ÷ 累计 Install'],['Payable spend',money(t.payable_spend),`Midas spend ${money(t.midas_spend)}`]];document.getElementById('cards').innerHTML=cards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
const budgetCards=[['月度预算','R '+fmt(b.monthly_budget_zar),money(b.monthly_budget_usd)],['已用预算','R '+fmt(b.used_budget_zar),pct(b.used_rate)+' of month'],['剩余预算','R '+fmt(b.remaining_budget_zar),money(b.remaining_budget_usd)],['剩余日均需求','R '+fmt(b.required_daily_budget_zar),`${fmt(b.required_daily_events)} E1+E2 / day`]];document.getElementById('budgetCards').innerHTML=budgetCards.map(x=>`<div class="card"><div class="label">${x[0]}</div><div class="value">${x[1]}</div><div class="note">${x[2]}</div></div>`).join('');
document.getElementById('budgetText').innerHTML=`<strong>预算消耗低于截至当前日期的均匀进度。</strong> 已用 R ${fmt(b.used_budget_zar)},同期预算应为 R ${fmt(b.expected_budget_to_date_zar)};剩余 ${b.remaining_days} 天需要日均 R ${fmt(b.required_daily_budget_zar)},高于当前日预算 R ${fmt(b.daily_budget_zar)}。`;
document.getElementById('budgetProgress').style.width=Math.min(b.used_rate,100)+'%';document.getElementById('budgetUsed').textContent=`已用 ${pct(b.used_rate)} · R ${fmt(b.used_budget_zar)}`;document.getElementById('budgetTotal').textContent=`总预算 R ${fmt(b.monthly_budget_zar)}`;
bars(document.getElementById('deliveryChart'),d,[{key:'install',color:'var(--blue)',format:fmt},{key:'event1',color:'var(--gold)',format:fmt},{key:'event2',color:'var(--orange)',format:fmt}],Math.max(...d.flatMap(x=>[x.install,x.event1,x.event2]),1)); bars(document.getElementById('deliveryChart'),d,[{key:'install',color:'var(--blue)',format:fmt},{key:'event1',color:'var(--gold)',format:fmt},{key:'event2',color:'var(--orange)',format:fmt}],Math.max(...d.flatMap(x=>[x.install,x.event1,x.event2]),1));
bars(document.getElementById('rateChart'),d,[{key:'e1e2_rate',color:'var(--orange)',format:pct}],Math.max(...d.map(x=>x.e1e2_rate||0),1)); bars(document.getElementById('rateChart'),d,[{key:'e1e2_rate',color:'var(--orange)',format:pct}],Math.max(...d.map(x=>x.e1e2_rate||0),1));
bars(document.getElementById('spendChart'),d,[{key:'midas_spend',color:'var(--blue)',format:money},{key:'payable_spend',color:'var(--gold)',format:money}],Math.max(...d.flatMap(x=>[x.midas_spend,x.payable_spend]),1)); bars(document.getElementById('spendChart'),d,[{key:'midas_spend',color:'var(--blue)',format:money},{key:'payable_spend',color:'var(--gold)',format:money}],Math.max(...d.flatMap(x=>[x.midas_spend,x.payable_spend]),1));
const gap=t.payable_spend-t.midas_spend;document.getElementById('spendText').innerHTML=`<strong>累计 Payable spend ${gap>=0?'高于':'低于'} Midas spend ${money(Math.abs(gap))}。</strong> 该差额来自约定的事件计费公式与平台实际消耗之间的口径差异,不应直接解读为利润。`; const gap=t.payable_spend-t.midas_spend;document.getElementById('spendText').innerHTML=`<strong>累计 Payable spend ${gap>=0?'高于':'低于'} Midas spend ${money(Math.abs(gap))}。</strong> 该差额来自约定的事件计费公式与平台实际消耗之间的口径差异,不应直接解读为利润。`;
document.getElementById('rows').innerHTML=d.slice().reverse().map(x=>`<tr><td>${x.date}</td><td>${fmt(x.install)}</td><td>${fmt(x.event1)}</td><td>${fmt(x.event2)}</td><td>${pct(x.e1e2_rate)}</td><td>${money(x.midas_spend)}</td><td>${money(x.payable_spend)}</td></tr>`).join(''); document.getElementById('rows').innerHTML=all.map(x=>`<tr class="${x.is_actual?'':'future'}"><td>${x.date}</td><td>${fmt(x.install)}</td><td>${fmt(x.event1)}</td><td>${fmt(x.event2)}</td><td>${fmt(x.e1e2)}</td><td>${pct(x.e1e2_rate)}</td><td>R ${fmt(x.payable_spend_zar)}</td><td>R ${fmt(x.daily_budget_zar)}</td><td>${pct(x.ffr_rate)}</td><td>${money(x.midas_spend)}</td><td>${x.ecpi_usd==null?'—':money(x.ecpi_usd)}</td></tr>`).join('');
}).catch(e=>{document.getElementById('summaryText').className='error';document.getElementById('summaryText').textContent='数据载入失败:'+e.message})} }).catch(e=>{document.getElementById('summaryText').className='error';document.getElementById('summaryText').textContent='数据载入失败:'+e.message})}
document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const email=document.getElementById('email').value.trim().toLowerCase(),password=document.getElementById('password').value;const [emailHash,passwordHash]=await Promise.all([sha256(email),sha256(ACCESS.salt+':'+password)]);if(ACCESS.emails.includes(emailHash)&&passwordHash===ACCESS.password){sessionStorage.setItem('amazonZaAccess','granted');showReport()}else{error.textContent='邮箱或密码不正确。'}}catch(_){error.textContent='当前浏览器无法完成验证,请更换现代浏览器。'}finally{button.disabled=false}}); document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const email=document.getElementById('email').value.trim().toLowerCase(),password=document.getElementById('password').value;const [emailHash,passwordHash]=await Promise.all([sha256(email),sha256(ACCESS.salt+':'+password)]);if(ACCESS.emails.includes(emailHash)&&passwordHash===ACCESS.password){sessionStorage.setItem('amazonZaAccess','granted');showReport()}else{error.textContent='邮箱或密码不正确。'}}catch(_){error.textContent='当前浏览器无法完成验证,请更换现代浏览器。'}finally{button.disabled=false}});
if(sessionStorage.getItem('amazonZaAccess')==='granted')showReport(); if(sessionStorage.getItem('amazonZaAccess')==='granted')showReport();
+5886 -11
View File
File diff suppressed because it is too large Load Diff
+2383 -53
View File
File diff suppressed because it is too large Load Diff
+65 -9
View File
@@ -9,12 +9,18 @@ import csv
import json import json
import os import os
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timezone from datetime import date, datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
root = Path(os.environ["REPORT_DIR"]) root = Path(os.environ["REPORT_DIR"])
package = "com.amazon.mShop.android.shopping" package = "com.amazon.mShop.android.shopping"
geo = "ZA" geo = "ZA"
launch_start = date(2026, 8, 1)
launch_end = date(2026, 8, 31)
monthly_budget_zar = 511_500.0
daily_budget_zar = 16_500.0
unit_price_zar = 49.5
fx_zar_per_usd = 16.5
spend = defaultdict(float) spend = defaultdict(float)
with (root / "source-delivery.csv").open(encoding="utf-8-sig", newline="") as f: with (root / "source-delivery.csv").open(encoding="utf-8-sig", newline="") as f:
@@ -43,34 +49,82 @@ with (root / "source-events.csv").open(encoding="utf-8-sig", newline="") as f:
key = "install" if name == "af_app_opened" else name key = "install" if name == "af_app_opened" else name
events[day][key] += repostback events[day][key] += repostback
days = sorted(set(spend) | set(events)) source_days = sorted(set(spend) | set(events))
data_cutoff = source_days[-1] if source_days else None
days = []
cursor = launch_start
while cursor <= launch_end:
days.append(cursor.strftime("%Y%m%d"))
cursor += timedelta(days=1)
daily = [] daily = []
for day in days: for day in days:
install = events[day]["install"] install = events[day]["install"]
event1 = events[day]["event1"] event1 = events[day]["event1"]
event2 = events[day]["event2"] event2 = events[day]["event2"]
conversions = event1 + event2 conversions = event1 + event2
payable_zar = conversions * unit_price_zar
midas_spend_usd = round(spend[day], 2)
is_actual = bool(data_cutoff and day <= data_cutoff)
daily.append({ daily.append({
"date": f"{day[:4]}-{day[4:6]}-{day[6:]}", "date": f"{day[:4]}-{day[4:6]}-{day[6:]}",
"is_actual": is_actual,
"install": install, "install": install,
"event1": event1, "event1": event1,
"event2": event2, "event2": event2,
"e1e2": conversions, "e1e2": conversions,
"e1e2_rate": round(conversions / install * 100, 2) if install else None, "e1e2_rate": round(conversions / install * 100, 2) if install else None,
"midas_spend": round(spend[day], 2), "midas_spend": midas_spend_usd,
"midas_spend_zar": round(midas_spend_usd * fx_zar_per_usd, 2),
"payable_spend": round(conversions * 3, 2), "payable_spend": round(conversions * 3, 2),
"payable_spend_zar": round(payable_zar, 2),
"daily_budget_zar": daily_budget_zar,
"daily_cap": round(daily_budget_zar / unit_price_zar),
"ffr_rate": round(conversions / (daily_budget_zar / unit_price_zar) * 100, 2),
"ecpi_usd": round(midas_spend_usd / install, 4) if install else None,
}) })
actual_daily = [x for x in daily if x["is_actual"]]
totals = { totals = {
"install": sum(x["install"] for x in daily), "install": sum(x["install"] for x in actual_daily),
"event1": sum(x["event1"] for x in daily), "event1": sum(x["event1"] for x in actual_daily),
"event2": sum(x["event2"] for x in daily), "event2": sum(x["event2"] for x in actual_daily),
"e1e2": sum(x["e1e2"] for x in daily), "e1e2": sum(x["e1e2"] for x in actual_daily),
"midas_spend": round(sum(x["midas_spend"] for x in daily), 2), "midas_spend": round(sum(x["midas_spend"] for x in actual_daily), 2),
"payable_spend": round(sum(x["payable_spend"] for x in daily), 2), "midas_spend_zar": round(sum(x["midas_spend_zar"] for x in actual_daily), 2),
"payable_spend": round(sum(x["payable_spend"] for x in actual_daily), 2),
"payable_spend_zar": round(sum(x["payable_spend_zar"] for x in actual_daily), 2),
} }
totals["e1e2_rate"] = round(totals["e1e2"] / totals["install"] * 100, 2) if totals["install"] else None totals["e1e2_rate"] = round(totals["e1e2"] / totals["install"] * 100, 2) if totals["install"] else None
elapsed_days = len(actual_daily)
remaining_days = max((launch_end - datetime.strptime(data_cutoff, "%Y%m%d").date()).days, 0) if data_cutoff else (launch_end - launch_start).days + 1
expected_budget_to_date = min(elapsed_days * daily_budget_zar, monthly_budget_zar)
remaining_budget_zar = monthly_budget_zar - totals["payable_spend_zar"]
avg_daily_used = totals["payable_spend_zar"] / elapsed_days if elapsed_days else 0
projected_month_spend = avg_daily_used * ((launch_end - launch_start).days + 1)
budget = {
"monthly_budget_zar": monthly_budget_zar,
"monthly_budget_usd": round(monthly_budget_zar / fx_zar_per_usd, 2),
"daily_budget_zar": daily_budget_zar,
"daily_budget_usd": round(daily_budget_zar / fx_zar_per_usd, 2),
"unit_price_zar": unit_price_zar,
"unit_price_usd": round(unit_price_zar / fx_zar_per_usd, 2),
"fx_zar_per_usd": fx_zar_per_usd,
"daily_cap": round(daily_budget_zar / unit_price_zar),
"used_budget_zar": totals["payable_spend_zar"],
"used_rate": round(totals["payable_spend_zar"] / monthly_budget_zar * 100, 2),
"remaining_budget_zar": round(remaining_budget_zar, 2),
"remaining_budget_usd": round(remaining_budget_zar / fx_zar_per_usd, 2),
"expected_budget_to_date_zar": expected_budget_to_date,
"pacing_rate": round(totals["payable_spend_zar"] / expected_budget_to_date * 100, 2) if expected_budget_to_date else None,
"elapsed_days": elapsed_days,
"remaining_days": remaining_days,
"required_daily_budget_zar": round(remaining_budget_zar / remaining_days, 2) if remaining_days else 0,
"required_daily_events": round(remaining_budget_zar / remaining_days / unit_price_zar) if remaining_days else 0,
"projected_month_spend_zar": round(projected_month_spend, 2),
"projected_month_variance_zar": round(monthly_budget_zar - projected_month_spend, 2),
}
payload = { payload = {
"meta": { "meta": {
"package": package, "package": package,
@@ -78,6 +132,7 @@ payload = {
"generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), "generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"date_start": daily[0]["date"] if daily else None, "date_start": daily[0]["date"] if daily else None,
"date_end": daily[-1]["date"] if daily else None, "date_end": daily[-1]["date"] if daily else None,
"data_cutoff": f"{data_cutoff[:4]}-{data_cutoff[4:6]}-{data_cutoff[6:]}" if data_cutoff else None,
"currency": "USD", "currency": "USD",
"install_definition": "af_app_opened repostback", "install_definition": "af_app_opened repostback",
"event_definition": "event1/event2 repostback", "event_definition": "event1/event2 repostback",
@@ -85,6 +140,7 @@ payload = {
"payable_definition": "(event1 + event2) × $3", "payable_definition": "(event1 + event2) × $3",
}, },
"totals": totals, "totals": totals,
"budget": budget,
"daily": daily, "daily": daily,
} }