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
+65 -9
View File
@@ -9,12 +9,18 @@ import csv
import json
import os
from collections import defaultdict
from datetime import datetime, timezone
from datetime import date, datetime, timedelta, timezone
from pathlib import Path
root = Path(os.environ["REPORT_DIR"])
package = "com.amazon.mShop.android.shopping"
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)
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
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 = []
for day in days:
install = events[day]["install"]
event1 = events[day]["event1"]
event2 = events[day]["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({
"date": f"{day[:4]}-{day[4:6]}-{day[6:]}",
"is_actual": is_actual,
"install": install,
"event1": event1,
"event2": event2,
"e1e2": conversions,
"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_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 = {
"install": sum(x["install"] for x in daily),
"event1": sum(x["event1"] for x in daily),
"event2": sum(x["event2"] for x in daily),
"e1e2": sum(x["e1e2"] for x in daily),
"midas_spend": round(sum(x["midas_spend"] for x in daily), 2),
"payable_spend": round(sum(x["payable_spend"] for x in daily), 2),
"install": sum(x["install"] for x in actual_daily),
"event1": sum(x["event1"] for x in actual_daily),
"event2": sum(x["event2"] for x in actual_daily),
"e1e2": sum(x["e1e2"] for x in actual_daily),
"midas_spend": round(sum(x["midas_spend"] for x in actual_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
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 = {
"meta": {
"package": package,
@@ -78,6 +132,7 @@ payload = {
"generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"date_start": daily[0]["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",
"install_definition": "af_app_opened repostback",
"event_definition": "event1/event2 repostback",
@@ -85,6 +140,7 @@ payload = {
"payable_definition": "(event1 + event2) × $3",
},
"totals": totals,
"budget": budget,
"daily": daily,
}