This notebook provides some insights on the distribution of funding to the campaigns in the gaza-verified mutual aid program.
It analyzes:
We first look at the cumulative donation trends over the past week
# Common imports and constants
import requests
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
api_url = "https://gaza.onl/api/v1/campaigns/accounts"
end_time = str(datetime.now().date() - timedelta(days=1))
# Fetch the total donation amounts, grouped by date
start_time = str(datetime.now().date() - timedelta(days=30))
group_by = ["donation.created_at:day"]
sort = "donation.created_at"
response = requests.get(
api_url,
params={
"start_time": start_time,
"end_time": end_time,
"group_by": group_by,
"sort": sort,
},
)
response.raise_for_status()
data = response.json().get('data')
# Group them up and keep a separate day -> n_campaigns map (used later for the mean value)
donations_per_day = {}
campaigns_per_day = {}
for account_record in data:
for row in account_record.get('data', []):
account, date = row['group_value']
amount = row['amount']['amount']
if date:
donations_per_day[date] = donations_per_day.get(date, 0) + amount
campaigns_per_day[date] = {*campaigns_per_day.get(date, set()), account}
campaigns_per_day = {
item[0]: len(item[1])
for item in sorted(
campaigns_per_day.items(),
key=lambda pair: pair[0],
)
}
donations_per_day = [
(item[0], int(item[1]))
for item in sorted(
donations_per_day.items(),
key=lambda pair: pair[0],
)
]
dates, amounts = zip(*donations_per_day)
amounts = np.array(amounts)
plt.figure(figsize=(12, 8))
plt.plot(
dates, amounts, marker=".", linestyle="-", label="Daily Donations", color="orange"
)
# Add moving average over the last 3 days
window_size = 4
if len(amounts) >= window_size:
moving_avg = np.convolve(amounts, np.ones(window_size) / window_size, mode="valid")
plt.plot(
dates[window_size - 1 :],
moving_avg,
color="blue",
linestyle="--",
label=f"{window_size}-Day Moving Average",
)
plt.fill_between(dates[window_size - 1 :], moving_avg, color="skyblue", alpha=0.4)
plt.legend()
plt.xlabel("Date")
plt.ylabel("Amount (USD)")
plt.title(f"Total Daily Donations from {start_time} to {end_time}")
plt.xticks(rotation=45)
plt.grid(True, alpha=0.3)
plt.gca().xaxis.set_major_locator(plt.MaxNLocator(nbins=len(dates)//2))
plt.tight_layout()
plt.show()
Let's take a closer look at how much support each campaign received on average, on a daily basis.
# Plot per-campaign average trend
pro_capita_donations_per_day = [
(date, amount / campaigns_per_day[date])
for date, amount in donations_per_day
if campaigns_per_day.get(date)
]
dates, amounts = zip(*pro_capita_donations_per_day)
amounts = np.array(amounts)
plt.figure(figsize=(12, 8))
plt.plot(
dates, amounts, marker=".", linestyle="-", label="Avg per-campaign daily donations", color="orange"
)
# Add moving average over the last 3 days
window_size = 4
if len(amounts) >= window_size:
moving_avg = np.convolve(amounts, np.ones(window_size) / window_size, mode="valid")
plt.plot(
dates[window_size - 1 :],
moving_avg,
color="blue",
linestyle="--",
label=f"{window_size}-Day Moving Average",
)
plt.fill_between(dates[window_size - 1 :], moving_avg, color="skyblue", alpha=0.4)
plt.legend()
plt.xlabel("Date")
plt.ylabel("Amount (USD)")
plt.title(f"Average Daily Donations Per Campaign from {start_time} to {end_time}")
plt.xticks(rotation=45)
plt.gca().xaxis.set_major_locator(plt.MaxNLocator(nbins=len(dates)//2))
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Finally, let's take a closer look at how fairly funding is distributed across the campaigns
start_time = str(datetime.now().date() - timedelta(days=7))
group_by = ["donation.campaign_url"]
sort = "amount:desc"
response = requests.get(
api_url,
params={
"start_time": start_time,
"group_by": group_by,
"sort": sort,
},
)
response.raise_for_status()
data = response.json().get('data')
amount_by_account = []
for row in data:
account = row['group_value'][0].split('/')[-1]
amount = row['amount']['amount']
amount_by_account.append((account, amount))
accounts, amounts = zip(*amount_by_account)
amounts = np.array(amounts)
plt.figure(figsize=(12, 8))
sns.barplot(
x=list(accounts),
y=amounts,
palette=sns.color_palette("RdYlGn", len(amounts)),
hue=[amount / max(amounts) for amount in amounts],
)
plt.xlabel("")
plt.ylabel("Amount raised (USD)")
plt.title(f"Distribution of funds raised by the campaigns between {start_time} and {datetime.now().date()}")
plt.xticks(rotation=45, labels=["" for _ in accounts], ticks=["" for _ in accounts])
plt.grid(True, alpha=0.3)
plt.legend([], [], frameon=False)
plt.tight_layout()
plt.show()