1- #!/usr/bin/env python3
2- """
1+ #!/usr/bin/env python
2+ """Script for processing American Express activity CSV into cleaned Excel file.
3+
34Process American Express activity CSV into a cleaned Excel file with columns:
45- Date (transaction date)
56- Source ("American Express Cobalt")
89- Anvita Paid (amount if Card Member is Anvita Akkur; else 0)
910- Taylor Portion (0.6 if groceries; else blank)
1011
11- Usage:
12- python process_amex_expenses.py --input activity.csv \\
13- --output amex_expenses_full.xlsx
12+ Usage Examples:
13+ # Basic usage
14+ python -m scripts.process_amex_expenses --input activity.csv --output expenses.xlsx
15+
16+ # Show help
17+ python -m scripts.process_amex_expenses --help
18+
19+ Requirements:
20+ - pandas library for CSV/Excel processing
21+ - Input CSV must have columns: Date, Description, Card Member, Amount
22+ - Positive amounts are treated as expenses, negative as payments/credits
1423"""
1524
1625import argparse
@@ -58,11 +67,26 @@ def is_grocery(merchant: str) -> bool:
5867 return any (pattern .search (merchant ) for pattern in GROCERY_PATTERNS )
5968
6069
70+ def paid_for (row : pd .Series [Any ], who : str ) -> float :
71+ """Calculate amount paid by specific person based on Card Member field."""
72+ cm = str (row .get ("Card Member" , "" )).upper ()
73+ return float (row ["Amount" ]) if who in cm else 0.0
74+
75+
6176def main () -> None :
62- p = argparse .ArgumentParser ()
63- p .add_argument ("--input" , "-i" , required = True , help = "Path to Amex activity CSV" )
64- p .add_argument ("--output" , "-o" , required = True , help = "Path to output Excel file" )
65- args = p .parse_args ()
77+ """Main function for process_amex_expenses."""
78+ parser = argparse .ArgumentParser (
79+ description = "Process American Express activity CSV into cleaned Excel file"
80+ )
81+
82+ parser .add_argument (
83+ "--input" , "-i" , type = str , required = True , help = "Path to Amex activity CSV"
84+ )
85+ parser .add_argument (
86+ "--output" , "-o" , type = str , required = True , help = "Path to output Excel file"
87+ )
88+
89+ args = parser .parse_args ()
6690
6791 # Read CSV (expects columns like: Date, Date Processed, Description,
6892 # Card Member, Account #, Amount)
@@ -72,8 +96,8 @@ def main() -> None:
7296 print (f"Error reading input CSV: { e } " , file = sys .stderr )
7397 sys .exit (1 )
7498
75- # Keep only expenses (positive amounts). Payments/credits are negative in
76- # Amex export.
99+ # Keep only expenses (positive amounts).
100+ # Payments/credits are negative in Amex export.
77101 if "Amount" not in df .columns :
78102 print ("Input CSV missing 'Amount' column." , file = sys .stderr )
79103 sys .exit (1 )
@@ -85,26 +109,24 @@ def main() -> None:
85109 print (f"Input CSV missing '{ col } ' column." , file = sys .stderr )
86110 sys .exit (1 )
87111
88- # Transform
112+ # Transform data
89113 expenses ["Expense" ] = expenses ["Description" ].apply (clean_description )
90114 expenses ["Source" ] = "American Express Cobalt"
91-
92- def paid_for (row : pd .Series [Any ], who : str ) -> float :
93- cm = str (row .get ("Card Member" , "" )).upper ()
94- return float (row ["Amount" ]) if who in cm else 0.0
95-
96115 expenses ["Taylor Paid" ] = expenses .apply (lambda r : paid_for (r , TAYLOR_NAME ), axis = 1 )
97116 expenses ["Anvita Paid" ] = expenses .apply (lambda r : paid_for (r , ANVITA_NAME ), axis = 1 )
98117 expenses ["Taylor Portion" ] = expenses ["Expense" ].apply (
99118 lambda x : 0.6 if is_grocery (x ) else ""
100119 )
101120
121+ # Select output columns
102122 out = expenses [
103123 ["Date" , "Source" , "Expense" , "Taylor Paid" , "Anvita Paid" , "Taylor Portion" ]
104124 ].copy ()
105125
126+ # Write Excel file
106127 try :
107128 out .to_excel (args .output , index = False )
129+ print (f"Successfully processed { len (out )} expenses to { args .output } " )
108130 except Exception as e :
109131 print (f"Error writing Excel: { e } " , file = sys .stderr )
110132 sys .exit (1 )
0 commit comments