| @@ -8,9 +8,13 @@ import os | |||
| import sys | |||
| import glob | |||
| import re | |||
| import argparse | |||
| import logging | |||
| import pandas as pd | |||
| from pathlib import Path | |||
| logger = logging.getLogger(__name__) | |||
| def parse_norwegian_number(value): | |||
| """Convert Norwegian number format (comma decimal) to float""" | |||
| if pd.isna(value) or value == '': | |||
| @@ -30,7 +34,7 @@ def parse_norwegian_date(date_str): | |||
| # Parse DD.MM.YYYY and convert to date object | |||
| return pd.to_datetime(date_str, format='%d.%m.%Y') | |||
| except (ValueError, TypeError): | |||
| print(f"Invalid date format: {date_str}") | |||
| logger.error(f"Invalid date format: {date_str}") | |||
| exit(1) | |||
| def convert_memo(original): | |||
| @@ -184,7 +188,7 @@ def process_bank_statement(file_path, parse_function, delimiter, encoding): | |||
| elif file_extension in [".xlsx", ".xls"]: | |||
| data = pd.read_excel(file_path) | |||
| else: | |||
| print(f"Skipping unsupported file type: {file_path}") | |||
| logger.warning(f"Skipping unsupported file type: {file_path}") | |||
| return pd.DataFrame() | |||
| # Call the appropriate bank-specific parsing function | |||
| @@ -192,7 +196,7 @@ def process_bank_statement(file_path, parse_function, delimiter, encoding): | |||
| return ynab_data | |||
| except Exception as e: | |||
| print(f"Error processing file {file_path}: {e}") | |||
| logger.error(f"Error processing file {file_path}: {e}") | |||
| raise e | |||
| return pd.DataFrame() | |||
| @@ -217,26 +221,30 @@ def find_bank_config(filename): | |||
| return None, None | |||
| def convert_bank_statements_to_ynab(input_files=None): | |||
| def convert_bank_statements_to_ynab(input_files, output_directory): | |||
| """ | |||
| Convert bank statements to YNAB format | |||
| Args: | |||
| input_files (list): Optional list of specific files to process | |||
| If None, processes all files in current directory | |||
| input_files (list): List of specific files to process | |||
| If empty, processes all files in current directory | |||
| """ | |||
| current_directory = Path.cwd() | |||
| output_directory = current_directory / "YNAB_Outputs" | |||
| if not output_directory: | |||
| output_directory = current_directory / "YNAB_Outputs" | |||
| logger.debug(f"No output directory set. Defaulting to {output_directory}") | |||
| # Create output directory if it doesn't exist | |||
| output_directory.mkdir(exist_ok=True) | |||
| output_directory.mkdir(exist_ok=True, parents=True) | |||
| # Get list of files to process | |||
| if input_files: | |||
| print(f"Processing {len(input_files)} dragged file(s)...") | |||
| logger.info(f"Processing {len(input_files)} dragged file(s)...") | |||
| files_to_process = [Path(f) for f in input_files if Path(f).exists()] | |||
| else: | |||
| print("Processing all files in current directory...") | |||
| logger.info("Processing all files in current directory...") | |||
| logger.debug(f"Current directory is {current_directory}") | |||
| files_to_process = [] | |||
| # Collect all files matching any bank pattern | |||
| for bank_config in BANKS.values(): | |||
| @@ -249,17 +257,17 @@ def convert_bank_statements_to_ynab(input_files=None): | |||
| # Process each file | |||
| for file_path in files_to_process: | |||
| if not file_path.exists(): | |||
| print(f"File not found: {file_path}") | |||
| logger.warning(f"File not found: {file_path}") | |||
| continue | |||
| # Find matching bank configuration | |||
| bank_name, bank_config = find_bank_config(file_path.name) | |||
| if not bank_config: | |||
| print(f"No bank configuration found for file: {file_path.name}") | |||
| logger.warning(f"No bank configuration found for file: {file_path.name}") | |||
| continue | |||
| print(f"Processing file: {file_path} for {bank_name}") | |||
| logger.info(f"Processing file: {file_path} for {bank_name}") | |||
| parse_function = bank_config["parse_function"] | |||
| delimiter = bank_config.get("delimiter", ",") | |||
| @@ -269,7 +277,7 @@ def convert_bank_statements_to_ynab(input_files=None): | |||
| ynab_data = process_bank_statement(str(file_path), parse_function, delimiter, encoding) | |||
| if ynab_data.empty: | |||
| print(f"No data processed for {file_path}") | |||
| logger.warning(f"No data processed for {file_path}") | |||
| continue | |||
| filename_placeholders = { | |||
| @@ -295,22 +303,49 @@ def convert_bank_statements_to_ynab(input_files=None): | |||
| # Export to CSV for YNAB import | |||
| ynab_data.to_csv(output_file, index=False) | |||
| print(f"Data saved to {output_file}") | |||
| logger.info(f"Data saved to {output_file}") | |||
| files_processed = True | |||
| if not files_processed: | |||
| print("No files were processed. Make sure your files match the expected patterns.") | |||
| logger.warning("No files were processed. Make sure your files match the expected patterns.") | |||
| if __name__ == "__main__": | |||
| # Check if files were dragged onto the script | |||
| if len(sys.argv) > 1: | |||
| # Files were dragged - process them | |||
| files = sys.argv[1:] | |||
| convert_bank_statements_to_ynab(files) | |||
| else: | |||
| # No files dragged - run normal directory processing | |||
| convert_bank_statements_to_ynab() | |||
| # Keep window open on Mac so user can see results | |||
| input("\nPress Enter to close...") | |||
| parser = argparse.ArgumentParser( | |||
| prog='YNAB', | |||
| description='Prepare bank transcripts for import to You Need A Budget', | |||
| ) | |||
| parser.add_argument( | |||
| 'filenames', | |||
| type=Path, | |||
| nargs='*', | |||
| help='The files to process', | |||
| ) | |||
| parser.add_argument( | |||
| '-o', '--output-dir', | |||
| type=Path, | |||
| default=None, | |||
| help='The location to store the converted files', | |||
| ) | |||
| parser.add_argument( | |||
| '-v', '--verbose', | |||
| default=0, | |||
| action='count', | |||
| help='Increase logging verbosity', | |||
| ) | |||
| args = parser.parse_args() | |||
| if args.verbose <= 0: | |||
| log_level = logging.WARNING | |||
| elif args.verbose == 1: | |||
| log_level = logging.INFO | |||
| elif args.verbose >= 2: | |||
| log_level = logging.DEBUG | |||
| logging.basicConfig(level=log_level) | |||
| convert_bank_statements_to_ynab(args.filenames, args.output_dir) | |||