Pass transaction table contents (without header) via stdin, get formatted CSV in stdout
Created
July 20, 2025 08:20
-
-
Save 0xKD/7766759ef7045e7de43e933a9d592b99 to your computer and use it in GitHub Desktop.
Infinia CC statement parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import csv | |
import re | |
from decimal import Decimal, ROUND_HALF_UP | |
DATE_RE = re.compile(r"(\d{2}/\d{2}/\d{4})(?:\s+(\d{2}:\d{2}:\d{2}))?\s+(.*)") | |
def parse_line(raw: str): | |
raw = raw.strip() | |
if not raw: | |
return None | |
m = DATE_RE.match(raw) | |
if not m: | |
return None # skip junk | |
date, time, rest = m.groups() | |
timestamp = date + (f" {time}" if time else "") | |
tokens = rest.split() | |
# Credit flag | |
is_credit = tokens[-1] == "Cr" | |
if is_credit: | |
tokens.pop() # drop "Cr" | |
amount = Decimal(tokens.pop().replace(",", "")) | |
# If credit, flip sign so later math is easy | |
if is_credit: | |
amount *= -1 | |
# Try to grab reward points (last numeric token left, if any) | |
reward = "" | |
for i in range(len(tokens) - 1, -1, -1): | |
if tokens[i].isdigit(): | |
reward = tokens[i] | |
del tokens[i] | |
# optional leading "-" that precedes reward points in some lines | |
if i - 1 >= 0 and tokens[i - 1] == "-": | |
del tokens[i - 1] | |
break | |
description = " ".join(tokens).strip() | |
return timestamp, description, reward, amount | |
def main(): | |
# Infinia statement parser (text copied from PDF) | |
rows = [] | |
debits = Decimal(0) | |
credits = Decimal(0) | |
for line in sys.stdin: | |
parsed = parse_line(line) | |
if parsed: | |
rows.append(parsed) | |
amt = parsed[3] | |
if amt >= 0: | |
debits += amt | |
else: | |
credits += -amt # store credits as positive for the summary | |
writer = csv.writer(sys.stdout, lineterminator="\n") | |
writer.writerow(["Timestamp", "Description", "RewardPoints", "Amount"]) | |
for ts, desc, pts, amt in rows: | |
# Write amount with sign preserved | |
writer.writerow( | |
[ts, desc, pts, f"{amt.quantize(Decimal('0.01'), ROUND_HALF_UP)}"] | |
) | |
# Blank line before summary | |
print() | |
net = debits - credits | |
print(f"Debits : {debits.quantize(Decimal('0.01'), ROUND_HALF_UP)}") | |
print(f"Credits: {credits.quantize(Decimal('0.01'), ROUND_HALF_UP)}") | |
print(f"Net : {net.quantize(Decimal('0.01'), ROUND_HALF_UP)}") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment