Generalize the last step into the reusable version: a null ledger driven by a policy registry, so the next export doesn't need hand-written ifs.
Write apply_null_policy(rows, policy) returning (cleaned, ledger):
policymaps column ->("fill", value),("keep", None), or("drop", None).- Walk rows in order. For each row, first check the drop columns: if any drop-column is null, the whole row is dropped (count it per column) and nothing else about that row is processed.
- Surviving rows are copied (never mutate the input), null fill-columns get the fill value (count per column), null keep-columns stay null (count per column).
ledgermaps each column inpolicyto its count.
The report loop is provided. Expected output:
plan: filled 1 with 'unknown'
mrr: filled 1 with 0.0
region: kept 2 null(s)
coupon: dropped 1 row(s)
rows in 4 -> rows out 3; every null has a decision