99- Taylor Portion (0.6 if groceries; else blank)
1010
1111Usage:
12- python process_amex_expenses.py --input activity.csv --output amex_expenses_full.xlsx
12+ python process_amex_expenses.py --input activity.csv \\
13+ --output amex_expenses_full.xlsx
1314"""
1415
1516import argparse
1617import re
1718import sys
18-
19+ from typing import Any
1920
2021import pandas as pd
2122
22-
2323# Regex patterns for grocery stores
2424GROCERY_PATTERNS = [
2525 re .compile (r"\bSAVE[-\s]?ON\b" , re .IGNORECASE ),
3737
3838
3939def clean_description (desc : str ) -> str :
40- """Simplify merchant description: remove store numbers, URLs, long numbers/phones, collapse spaces."""
40+ """Simplify merchant description: remove store numbers, URLs, long numbers/phones,
41+ collapse spaces."""
4142 if not isinstance (desc , str ):
4243 return ""
4344
@@ -57,20 +58,22 @@ def is_grocery(merchant: str) -> bool:
5758 return any (pattern .search (merchant ) for pattern in GROCERY_PATTERNS )
5859
5960
60- def main ():
61+ def main () -> None :
6162 p = argparse .ArgumentParser ()
6263 p .add_argument ("--input" , "-i" , required = True , help = "Path to Amex activity CSV" )
6364 p .add_argument ("--output" , "-o" , required = True , help = "Path to output Excel file" )
6465 args = p .parse_args ()
6566
66- # Read CSV (expects columns like: Date, Date Processed, Description, Card Member, Account #, Amount)
67+ # Read CSV (expects columns like: Date, Date Processed, Description,
68+ # Card Member, Account #, Amount)
6769 try :
6870 df = pd .read_csv (args .input )
6971 except Exception as e :
7072 print (f"Error reading input CSV: { e } " , file = sys .stderr )
7173 sys .exit (1 )
7274
73- # Keep only expenses (positive amounts). Payments/credits are negative in Amex export.
75+ # Keep only expenses (positive amounts). Payments/credits are negative in
76+ # Amex export.
7477 if "Amount" not in df .columns :
7578 print ("Input CSV missing 'Amount' column." , file = sys .stderr )
7679 sys .exit (1 )
@@ -86,13 +89,15 @@ def main():
8689 expenses ["Expense" ] = expenses ["Description" ].apply (clean_description )
8790 expenses ["Source" ] = "American Express Cobalt"
8891
89- def paid_for (row , who : str ) -> float :
92+ def paid_for (row : pd . Series [ Any ] , who : str ) -> float :
9093 cm = str (row .get ("Card Member" , "" )).upper ()
9194 return float (row ["Amount" ]) if who in cm else 0.0
9295
9396 expenses ["Taylor Paid" ] = expenses .apply (lambda r : paid_for (r , TAYLOR_NAME ), axis = 1 )
9497 expenses ["Anvita Paid" ] = expenses .apply (lambda r : paid_for (r , ANVITA_NAME ), axis = 1 )
95- expenses ["Taylor Portion" ] = expenses ["Expense" ].apply (lambda x : 0.6 if is_grocery (x ) else "" )
98+ expenses ["Taylor Portion" ] = expenses ["Expense" ].apply (
99+ lambda x : 0.6 if is_grocery (x ) else ""
100+ )
96101
97102 out = expenses [
98103 ["Date" , "Source" , "Expense" , "Taylor Paid" , "Anvita Paid" , "Taylor Portion" ]
0 commit comments