105 lines
2.6 KiB
Python
105 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import csv
|
|
from decimal import Decimal, InvalidOperation
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
|
|
def clean_money(value: str) -> Decimal | None:
|
|
if not value:
|
|
return None
|
|
|
|
value = (
|
|
value.replace("£", "")
|
|
.replace(",", "")
|
|
.strip()
|
|
)
|
|
|
|
if not value:
|
|
return None
|
|
|
|
try:
|
|
return Decimal(value)
|
|
except InvalidOperation:
|
|
return None
|
|
|
|
def parse_date(value: str) -> str | None:
|
|
"""
|
|
Convert '01 Dec 2025' → '12/01/2025'
|
|
"""
|
|
value = value.strip()
|
|
if not value:
|
|
return None
|
|
|
|
try:
|
|
dt = datetime.strptime(value, "%d %b %Y")
|
|
return dt.strftime("%m/%d/%Y")
|
|
except ValueError:
|
|
return None
|
|
|
|
def convert(in_path: Path, out_path: Path):
|
|
with open(in_path, newline="", encoding="latin-1") as f_in, \
|
|
open(out_path, "w", newline="", encoding="utf-8") as f_out:
|
|
|
|
reader = csv.reader(f_in)
|
|
|
|
# Skip metadata lines until we hit the header
|
|
for row in reader:
|
|
if row and row[0] == "Date":
|
|
header = row
|
|
break
|
|
else:
|
|
raise RuntimeError("Could not find Nationwide CSV header row")
|
|
|
|
dict_reader = csv.DictReader(f_in, fieldnames=header)
|
|
|
|
fieldnames = ["Date", "Payee", "Description", "Amount"]
|
|
writer = csv.DictWriter(f_out, fieldnames=fieldnames)
|
|
writer.writeheader()
|
|
|
|
for row in dict_reader:
|
|
raw_date = row.get("Date") or ""
|
|
date = parse_date(raw_date)
|
|
if not date:
|
|
continue
|
|
|
|
desc = (row.get("Description") or "").strip()
|
|
payee = desc
|
|
|
|
paid_out = clean_money(row.get("Paid out", ""))
|
|
paid_in = clean_money(row.get("Paid in", ""))
|
|
|
|
if paid_out is not None:
|
|
amount = -paid_out
|
|
elif paid_in is not None:
|
|
amount = paid_in
|
|
else:
|
|
continue
|
|
|
|
writer.writerow({
|
|
"Date": date,
|
|
"Payee": payee,
|
|
"Description": desc,
|
|
"Amount": f"{amount:.2f}",
|
|
})
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python nationwide-cleaner.py input.csv [output.csv]")
|
|
sys.exit(1)
|
|
|
|
in_path = Path(sys.argv[1])
|
|
out_path = Path(sys.argv[2]) if len(sys.argv) >= 3 else in_path.with_name(
|
|
in_path.stem + "_actual.csv"
|
|
)
|
|
|
|
convert(in_path, out_path)
|
|
print(f"Written cleaned file to {out_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|