diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..65ca21d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3 + +ADD converter.py / + +RUN pip install OfxParser +RUN pip install watchdog + +CMD [ "python", "./converter.py" ] \ No newline at end of file diff --git a/Loan.qfx b/Loan.qfx deleted file mode 100644 index 78e0f61..0000000 --- a/Loan.qfx +++ /dev/null @@ -1,87 +0,0 @@ -OFXHEADER:100 -DATA:OFXSGML -VERSION:102 -SECURITY:NONE -ENCODING:USASCII -CHARSET:1252 -COMPRESSION:NONE -OLDFILEUID:NONE -NEWFILEUID:NONE - - - - - - 0 - INFO - - 20220110041351 - - ENG - - HSBC Bank Australia - 10624 - - 10624 - XXXXXXXXXXXXXXXXXXXXXXXXXX - - - - - 0 - - 0 - INFO - - - AUD - - 001610020 - 342101 576544258 - CREDITLINE - - - - - - CREDIT - 20220106 - 20220106 - 1173.71 - 202200600001 - TRANSFER - FROM 099-271041-090 AUD IB1305543 INTERNET BANKING - - - CREDIT - 20220106 - 20220106 - 628.81 - 202200600002 - TRANSFER - FROM 099-271041-090 AUD IB1004824 INTERNET BANKING - - - DEBIT - 20220104 - 20220104 - -399440.42 - 202200400001 - TRANSFER - PEXA217198219-B2895881 LOAN DD 1592204/099-271041 DBCU00064 BANKING TERMINAL (UBT - - - - -397637.90 - 20220110041351 - - - - 1802.52 - 20220110041351 - - - - - - diff --git a/converted_Loan.csv b/converted_Loan.csv deleted file mode 100644 index c23949e..0000000 --- a/converted_Loan.csv +++ /dev/null @@ -1,4 +0,0 @@ -Date,Original Description,Category,Amount,Account Name -06/01/2022,FROM 099-271041-090 AUD IB1305543 INTERNET BANKING,Uncategorised,1173.71,HSBC Everyday Global -06/01/2022,FROM 099-271041-090 AUD IB1004824 INTERNET BANKING,Uncategorised,628.81,HSBC Everyday Global -04/01/2022,PEXA217198219-B2895881 LOAN DD 1592204/099-271041 DBCU00064 BANKING TERMINAL (UBT,Uncategorised,-399440.42,HSBC Everyday Global diff --git a/converter.ipynb b/converter.ipynb deleted file mode 100644 index 6feaeae..0000000 --- a/converter.ipynb +++ /dev/null @@ -1,92 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from ofxparse import OfxParser\n", - "import codecs" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "with codecs.open('qfx.qfx') as fileobj:\n", - " ofx = OfxParser.parse(fileobj)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'10624'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "account = ofx.account\n", - "\n", - "account.institution.fid" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Decimal('9570.86')" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "statement = account.statement\n", - "statement.available_balance" - ] - } - ], - "metadata": { - "interpreter": { - "hash": "8f1f8a1d9576cf73e2f91f0cdc8e61e6f80707111d98c220b357b8bb1eb61bce" - }, - "kernelspec": { - "display_name": "Python 3.8.12 64-bit ('hsbc': conda)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.12" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/converter.py b/converter.py index 83d2957..f7540dc 100644 --- a/converter.py +++ b/converter.py @@ -4,47 +4,100 @@ from csv import DictWriter from glob import glob from ofxparse import OfxParser +from pathlib import Path + +import os +import time +import watchdog.events +import watchdog.observers DATE_FORMAT = "%d/%m/%Y" +WATCH_DIR = os.path.dirname(os.path.realpath(__file__)) +PATTERN = '*.qfx' +BACKUP_DIR = 'Imported' -def write_csv(statement, out_file): - print("Writing: " + out_file) - fields = ['date', 'memo', 'category', 'amount', 'name'] - with open(out_file, 'w') as f: - f.write("Date,Original Description,Category,Amount,Account Name") - f.write("\r\n") - writer = DictWriter(f, fieldnames=fields) - for line in statement: - writer.writerow(line) - - -def get_statement_from_qfx(qfx): - balance = qfx.account.statement.balance - statement = [] - credit_transactions = ['credit', 'dep', 'int'] - debit_transactions = ['debit', 'atm', 'pos', 'xfer', 'check'] - for transaction in qfx.account.statement.transactions: - amount = "" - balance = balance + transaction.amount - - if transaction.payee.startswith("PENDING:"): - continue +class Handler(watchdog.events.PatternMatchingEventHandler): + def __init__(self): + # Set the patterns for PatternMatchingEventHandler + watchdog.events.PatternMatchingEventHandler.__init__(self, patterns=['*.qfx'], + ignore_directories=True, case_sensitive=False) + + @staticmethod + def write_csv(statement, out_file): + print("Writing: " + out_file) + fields = ['date', 'memo', 'category', 'amount', 'name'] + with open(out_file, 'w') as f: + f.write("Date,Original Description,Category,Amount,Account Name") + f.write("\r\n") + writer = DictWriter(f, fieldnames=fields) + for line in statement: + writer.writerow(line) - line = { - 'date': transaction.date.strftime(DATE_FORMAT), - 'memo' : transaction.memo, - 'category': 'Uncategorised', - 'amount': transaction.amount, - 'name': 'HSBC Everyday Global' - } - statement.append(line) - return statement - + @staticmethod + def get_statement_from_qfx(qfx): + balance = qfx.account.statement.balance -files = glob("*.qfx") -for qfx_file in files: - qfx = OfxParser.parse(open(qfx_file, encoding="latin-1"), fail_fast=False) - statement = get_statement_from_qfx(qfx) - out_file = "converted_" + qfx_file.replace(".qfx",".csv") - write_csv(statement, out_file) \ No newline at end of file + statement = [] + credit_transactions = ['credit', 'dep', 'int'] + debit_transactions = ['debit', 'atm', 'pos', 'xfer', 'check'] + for transaction in qfx.account.statement.transactions: + amount = "" + balance = balance + transaction.amount + + if transaction.payee.startswith("PENDING:"): + continue + + line = { + 'date': transaction.date.strftime(DATE_FORMAT), + 'memo' : transaction.memo, + 'category': 'Uncategorised', + 'amount': transaction.amount, + 'name': 'HSBC Everyday Global' + } + statement.append(line) + return statement + + @staticmethod + def unique_path(directory, name_pattern): + counter = 0 + while True: + counter += 1 + path = directory / name_pattern.format(counter) + if not path.exists(): + return path + + def on_created(self, event): + qfx = OfxParser.parse(open(event.src_path, encoding="latin-1"), fail_fast=False) + statement = Handler.get_statement_from_qfx(qfx) + + path = Path(event.src_path) + path.resolve() + + out_file = str(path.parent / ('converted' + path.stem + '.csv')) + Handler.write_csv(statement, out_file) + + #Now move the input file to backup + archive_file_dir = path.parent / BACKUP_DIR + archive_file = (path.stem + '{:04d}' + path.suffix) + destination = Handler.unique_path(archive_file_dir, archive_file) + + if not archive_file_dir.exists(): + archive_file_dir.mkdir() + + if not destination.exists(): + path.replace(destination) + + +if __name__ == "__main__": + event_handler = Handler() + observer = watchdog.observers.Observer() + observer.schedule(event_handler, path=WATCH_DIR, recursive=False) + + observer.start() + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + observer.join()