import json
import sys
from datetime import datetime
from pathlib import Path
CITY = {
("Milan", "Lagos"): 4030, ("Lagos", "Milan"): 4030,
("Milan", "Zurich"): 220, ("Zurich", "Milan"): 220,
}
def hhmmss(ts):
return datetime.strptime(ts, "%H:%M:%S")
def summarize(s):
u, dev, ips, evs = s["user"], s["device"], s["ips"], s["events"]
hops = []
prev = None
for e in evs:
ip = next((i for i in ips if i["ip"] == e["ip"]), None)
if ip and (prev is None or ip["city"] != prev["city"]):
if prev is not None:
dt = (hhmmss(e["ts"]) - hhmmss(hops[-1][1])).total_seconds() / 60
km = CITY.get((prev["city"], ip["city"]))
speed = f", {km/max(dt/60, 0.01):,.0f} km/h implied" if km else ""
hops.append((f"{prev['city']} -> {ip['city']} after {dt:.0f}m{speed}", e["ts"]))
else:
hops.append((f"{ip['country']}/{ip['city']}", e["ts"]))
prev = ip
geo = "; ".join(h[0] for h in hops)
new_ips = sum(1 for i in ips if not i.get("seen_before"))
if new_ips:
geo += f"; {new_ips} of {len(ips)} IPs never seen for this user"
asns = {i["asn"].split()[0] for i in ips}
if len(ips) > 1 and len(asns) == 1:
geo += "; all IPs in one ASN"
acts, out = [], 0
for e in evs:
p = e["path"].replace("/auth/", "").replace("/settings/", "settings ").strip("/")
d = e.get("detail", {})
if "amount_eur" in d:
out += d["amount_eur"]
bal = f" of {d['balance_before_eur']} balance" if "balance_before_eur" in d else ""
age = d.get("beneficiary_age_days")
who = "new beneficiary" if age == 0 else f"beneficiary saved {age}d ago" if age else d.get("beneficiary", "")
acts.append(f"transfer {d['amount_eur']} EUR{bal} to {who}")
elif "beneficiary" in d:
acts.append("added new beneficiary")
elif "inbound_last_hour_eur" in d:
acts.append(f"received {d['inbound_last_hour_eur']} EUR from {d['distinct_senders']} senders in 1h")
elif "balance_eur" in d:
acts.append(f"balance now {d['balance_eur']} EUR")
else:
acts.append(p)
dur = (hhmmss(evs[-1]["ts"]) - hhmmss(evs[0]["ts"])).total_seconds()
tags = [t["tag"] if isinstance(t, dict) else t for t in s.get("tags", [])]
operator = [t["tag"] for t in s.get("tags", []) if isinstance(t, dict) and t["source"].startswith("operator")]
return {
"tags": tags or ["none"],
"operator_notes": operator or ["none"],
"geo": geo,
"device": ("known" if dev.get("known") else "NEW, never used by this user") + f" ({dev['os']})",
"user": f"{u['segment']}, account {u['account_age_days']}d old, mfa "
+ ("enrolled" if u["enrolled_mfa"] else "NOT enrolled"),
"session_minutes": round(dur / 60, 1),
"money_out_eur": out,
"actions_in_order": acts,
}
if __name__ == "__main__":
for p in map(Path, sys.argv[1:]):
out = p.parent.parent / "compact" / p.name
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(summarize(json.loads(p.read_text())), indent=2) + "\n")
print(f"{p.name} -> {out}")