#!/usr/bin/env python3 import csv from decimal import Decimal, InvalidOperation from pathlib import Path from datetime import datetime import sys RAW_DIR = Path("/home/zaine/master-folder/attachments/bank-statements/nationwide/raw") CLEANED_DIR = Path("/home/zaine/master-folder/attachments/bank-statements/nationwide/cleaned") 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 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 select_file(files: list[Path]) -> Path: print("\nSelect a Nationwide CSV to clean:\n") for i, f in enumerate(files, start=1): print(f"[{i}] {f.name}") try: choice = int(input("\nEnter number: ").strip()) return files[choice - 1] except (ValueError, IndexError): print("Invalid selection.") sys.exit(1) def main(): if not RAW_DIR.exists(): print(f"Raw directory does not exist: {RAW_DIR}") sys.exit(1) CLEANED_DIR.mkdir(parents=True, exist_ok=True) csv_files = sorted(RAW_DIR.glob("*.csv")) if not csv_files: print("No CSV files found in raw directory.") sys.exit(0) in_path = select_file(csv_files) out_name = f"{in_path.stem}-cleaned.csv" out_path = CLEANED_DIR / out_name convert(in_path, out_path) print(f"\n✔ Written cleaned file to:\n{out_path}\n") if __name__ == "__main__": main()