Added watchdog to watch for changes and automatically convert
This commit is contained in:
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM python:3
|
||||||
|
|
||||||
|
ADD converter.py /
|
||||||
|
|
||||||
|
RUN pip install OfxParser
|
||||||
|
RUN pip install watchdog
|
||||||
|
|
||||||
|
CMD [ "python", "./converter.py" ]
|
||||||
87
Loan.qfx
87
Loan.qfx
@@ -1,87 +0,0 @@
|
|||||||
OFXHEADER:100
|
|
||||||
DATA:OFXSGML
|
|
||||||
VERSION:102
|
|
||||||
SECURITY:NONE
|
|
||||||
ENCODING:USASCII
|
|
||||||
CHARSET:1252
|
|
||||||
COMPRESSION:NONE
|
|
||||||
OLDFILEUID:NONE
|
|
||||||
NEWFILEUID:NONE
|
|
||||||
|
|
||||||
<OFX>
|
|
||||||
<SIGNONMSGSRSV1>
|
|
||||||
<SONRS>
|
|
||||||
<STATUS>
|
|
||||||
<CODE>0
|
|
||||||
<SEVERITY>INFO
|
|
||||||
</STATUS>
|
|
||||||
<DTSERVER>20220110041351
|
|
||||||
|
|
||||||
<LANGUAGE>ENG
|
|
||||||
<FI>
|
|
||||||
<ORG>HSBC Bank Australia
|
|
||||||
<FID>10624
|
|
||||||
</FI>
|
|
||||||
<INTU.BID>10624
|
|
||||||
<INTU.USERID>XXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
||||||
</SONRS>
|
|
||||||
</SIGNONMSGSRSV1>
|
|
||||||
<BANKMSGSRSV1>
|
|
||||||
<STMTTRNRS>
|
|
||||||
<TRNUID>0
|
|
||||||
<STATUS>
|
|
||||||
<CODE>0
|
|
||||||
<SEVERITY>INFO
|
|
||||||
</STATUS>
|
|
||||||
<STMTRS>
|
|
||||||
<CURDEF>AUD
|
|
||||||
<BANKACCTFROM>
|
|
||||||
<BANKID>001610020
|
|
||||||
<ACCTID>342101 576544258
|
|
||||||
<ACCTTYPE>CREDITLINE
|
|
||||||
</BANKACCTFROM>
|
|
||||||
<BANKTRANLIST>
|
|
||||||
<DTSTART>
|
|
||||||
<DTEND>
|
|
||||||
<STMTTRN>
|
|
||||||
<TRNTYPE>CREDIT
|
|
||||||
<DTPOSTED>20220106
|
|
||||||
<DTUSER>20220106
|
|
||||||
<TRNAMT>1173.71
|
|
||||||
<FITID>202200600001
|
|
||||||
<NAME> TRANSFER
|
|
||||||
<MEMO>FROM 099-271041-090 AUD IB1305543 INTERNET BANKING
|
|
||||||
</STMTTRN>
|
|
||||||
<STMTTRN>
|
|
||||||
<TRNTYPE>CREDIT
|
|
||||||
<DTPOSTED>20220106
|
|
||||||
<DTUSER>20220106
|
|
||||||
<TRNAMT>628.81
|
|
||||||
<FITID>202200600002
|
|
||||||
<NAME> TRANSFER
|
|
||||||
<MEMO>FROM 099-271041-090 AUD IB1004824 INTERNET BANKING
|
|
||||||
</STMTTRN>
|
|
||||||
<STMTTRN>
|
|
||||||
<TRNTYPE>DEBIT
|
|
||||||
<DTPOSTED>20220104
|
|
||||||
<DTUSER>20220104
|
|
||||||
<TRNAMT>-399440.42
|
|
||||||
<FITID>202200400001
|
|
||||||
<NAME> TRANSFER
|
|
||||||
<MEMO>PEXA217198219-B2895881 LOAN DD 1592204/099-271041 DBCU00064 BANKING TERMINAL (UBT
|
|
||||||
</STMTTRN>
|
|
||||||
</BANKTRANLIST>
|
|
||||||
<LEDGERBAL>
|
|
||||||
<BALAMT>-397637.90
|
|
||||||
<DTASOF>20220110041351
|
|
||||||
|
|
||||||
</LEDGERBAL>
|
|
||||||
<AVAILBAL>
|
|
||||||
<BALAMT>1802.52
|
|
||||||
<DTASOF>20220110041351
|
|
||||||
|
|
||||||
</AVAILBAL>
|
|
||||||
</STMTRS>
|
|
||||||
</STMTTRNRS>
|
|
||||||
</BANKMSGSRSV1>
|
|
||||||
</OFX>
|
|
||||||
@@ -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
|
|
||||||
|
@@ -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
|
|
||||||
}
|
|
||||||
67
converter.py
67
converter.py
@@ -4,9 +4,26 @@
|
|||||||
from csv import DictWriter
|
from csv import DictWriter
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from ofxparse import OfxParser
|
from ofxparse import OfxParser
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import watchdog.events
|
||||||
|
import watchdog.observers
|
||||||
|
|
||||||
DATE_FORMAT = "%d/%m/%Y"
|
DATE_FORMAT = "%d/%m/%Y"
|
||||||
|
WATCH_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
PATTERN = '*.qfx'
|
||||||
|
BACKUP_DIR = 'Imported'
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
def write_csv(statement, out_file):
|
||||||
print("Writing: " + out_file)
|
print("Writing: " + out_file)
|
||||||
fields = ['date', 'memo', 'category', 'amount', 'name']
|
fields = ['date', 'memo', 'category', 'amount', 'name']
|
||||||
@@ -17,7 +34,7 @@ def write_csv(statement, out_file):
|
|||||||
for line in statement:
|
for line in statement:
|
||||||
writer.writerow(line)
|
writer.writerow(line)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_statement_from_qfx(qfx):
|
def get_statement_from_qfx(qfx):
|
||||||
balance = qfx.account.statement.balance
|
balance = qfx.account.statement.balance
|
||||||
|
|
||||||
@@ -41,10 +58,46 @@ def get_statement_from_qfx(qfx):
|
|||||||
statement.append(line)
|
statement.append(line)
|
||||||
return statement
|
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
|
||||||
|
|
||||||
files = glob("*.qfx")
|
def on_created(self, event):
|
||||||
for qfx_file in files:
|
qfx = OfxParser.parse(open(event.src_path, encoding="latin-1"), fail_fast=False)
|
||||||
qfx = OfxParser.parse(open(qfx_file, encoding="latin-1"), fail_fast=False)
|
statement = Handler.get_statement_from_qfx(qfx)
|
||||||
statement = get_statement_from_qfx(qfx)
|
|
||||||
out_file = "converted_" + qfx_file.replace(".qfx",".csv")
|
path = Path(event.src_path)
|
||||||
write_csv(statement, out_file)
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user