Compare commits
No commits in common. "9783960857240928d909d086a931a7d553930736" and "140f8fe6c7aa7313989df9c0f1c118b7e97b0a6f" have entirely different histories.
9783960857
...
140f8fe6c7
40 changed files with 334 additions and 5549 deletions
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import vanth.celery
|
||||
|
||||
vanth.celery.run()
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
|
||||
import vanth.main
|
||||
import vanth.ofxhome
|
||||
import vanth.platform.ofxsource
|
||||
|
||||
|
||||
def main():
|
||||
vanth.main.setup_logging()
|
||||
config = vanth.main.get_config()
|
||||
vanth.main.create_db_connection(config)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('dbfile', help='The database file of XML dumped from the open OFX Home DB')
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.dbfile, 'r') as f:
|
||||
data = vanth.ofxhome.parse(f.read())
|
||||
vanth.platform.ofxsource.ensure_exist(data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import vanth.celery
|
||||
|
||||
def main():
|
||||
vanth.celery.my_print.delay()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import sys
|
||||
|
||||
payload = {'username': 'eliribble', 'password': sys.argv[1]}
|
||||
response = requests.post('http://www.vanth.com/login/', data=payload)
|
||||
print(response.status_code, response.text)
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import vanth.main
|
||||
import vanth.download
|
||||
|
||||
def main():
|
||||
config = vanth.main.get_config()
|
||||
vanth.main.create_db_connection(config)
|
||||
print(vanth.download.do_all())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
6
bootstrap-theme.min.css
vendored
6
bootstrap-theme.min.css
vendored
File diff suppressed because one or more lines are too long
6
bootstrap.min.css
vendored
6
bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
7
bootstrap.min.js
vendored
7
bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,7 +0,0 @@
|
|||
BROKER_URL = 'amqp://guest:guest@localhost:5672//'
|
||||
|
||||
CELERY_IMPORTS = ('vanth.celery_worker', 'vanth.celery')
|
||||
CELERY_ACCEPT_CONTENT = ['json', 'msgpack', 'yaml']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
|
||||
CELERY_ALWAYS_EAGER = True
|
6
jquery.min.js
vendored
6
jquery.min.js
vendored
File diff suppressed because one or more lines are too long
82
ofx.py
82
ofx.py
|
@ -1,82 +0,0 @@
|
|||
import datetime
|
||||
import pprint
|
||||
import logging
|
||||
import ofxtools
|
||||
import io
|
||||
import requests
|
||||
import sys
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def parse(content):
|
||||
tree = ofxtools.Parser.OFXTree()
|
||||
tree.parse(io.StringIO(content))
|
||||
return tree
|
||||
|
||||
def get_transactions(tree):
|
||||
return [parse_transaction(transaction) for transaction in tree.findall('.//STMTTRN')]
|
||||
|
||||
def _parse_datetime(timestamp):
|
||||
return datetime.datetime.strptime(timestamp, '%Y%m%d%H%M%S.000')
|
||||
|
||||
def parse_transaction(transaction):
|
||||
return {
|
||||
'available' : _parse_datetime(transaction.find('./DTAVAIL').text),
|
||||
'amount' : float(transaction.find('./TRNAMT').text),
|
||||
'id' : transaction.find('./FITID').text,
|
||||
'memo' : transaction.find('./MEMO').text,
|
||||
'name' : transaction.find('./NAME').text,
|
||||
'posted' : _parse_datetime(transaction.find('./DTPOSTED').text),
|
||||
'type' : transaction.find('./TRNTYPE').text,
|
||||
}
|
||||
|
||||
def main():
|
||||
logging.basicConfig()
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
with open(sys.argv[1], 'r') as f:
|
||||
content = f.read()
|
||||
tree = parse(content)
|
||||
transactions = get_transactions(tree)
|
||||
for transaction in transactions[:10]:
|
||||
if transaction['amount'] > 0:
|
||||
continue
|
||||
pprint.pprint(transaction)
|
||||
payload = {
|
||||
'amount' : transaction['amount'],
|
||||
'currency' : {
|
||||
'code' : 'USD',
|
||||
'rate' : 1.0,
|
||||
'fixed' : False,
|
||||
},
|
||||
'date' : transaction['posted'].date().isoformat(),
|
||||
'desc' : transaction['name'],
|
||||
'account' : '2553488',
|
||||
'category' : '50448856',
|
||||
'tags' : [],
|
||||
'extra' : {
|
||||
'available' : transaction['available'].date().isoformat(),
|
||||
'fid' : transaction['id'],
|
||||
'memo' : transaction['memo'],
|
||||
'type' : transaction['type'],
|
||||
},
|
||||
}
|
||||
continue
|
||||
response = requests.post('https://api.toshl.com/entries', json=payload, auth=('4cc0e2c6-7b91-4198-9759-df7f873c4d0d4f44474d-e0e8-4e40-b755-7dfb71955cb2', ''))
|
||||
if not response.ok:
|
||||
print(response.status_code)
|
||||
print(response.text)
|
||||
return
|
||||
else:
|
||||
print("uploaded")
|
||||
pprint.pprint(transactions[:10])
|
||||
return
|
||||
root = tree.getroot()
|
||||
pprint(root)
|
||||
|
||||
def print_tree(root, indent=0):
|
||||
print("{}{}: {}".format('\t'*indent, root.tag, root.text if root.text else ''))
|
||||
for child in root.getchildren():
|
||||
pprint(child, indent=indent+1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1
setup.py
1
setup.py
|
@ -101,7 +101,6 @@ def main():
|
|||
'chryso==1.7',
|
||||
'Flask==0.10.1',
|
||||
'flask-login==0.3.2',
|
||||
'ofxparse==0.15',
|
||||
'sepiida==5.27',
|
||||
],
|
||||
extras_require = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% block main_content %}
|
||||
<h1>{{ account.name }}</h1>
|
||||
{% include 'update_button.html' %}
|
||||
<table class="table">
|
||||
<tr><td>Current balance</td><td>{{ account.balance }}</td></tr>
|
||||
<tr><td>Last updated</td><td>{{ account.last_updated }}</td></tr>
|
||||
|
@ -16,7 +15,7 @@
|
|||
<td>{{ record.name }}</a></td>
|
||||
<td>{{ record.type }}</td>
|
||||
<td>{{ record.amount }}</td>
|
||||
<td>{{ record.posted }}</td>
|
||||
<td>{{ account.posted }}</td>
|
||||
<td>
|
||||
<form method="POST" action="/update/">
|
||||
<input type="hidden" name="account_uuid" value="{{ account.uuid }}"></input>
|
||||
|
@ -29,10 +28,4 @@
|
|||
{% else %}
|
||||
<p>This account does not have any transactions yet</p>
|
||||
{% endif %}
|
||||
<form method="POST" action="/transactions/" enctype="multipart/form-data">
|
||||
<p>Want to upload your own transactions? Cool. Do it here.</p>
|
||||
<input type="hidden" name="account_uuid" value="{{ account.uuid }}">
|
||||
<input type="file" name="transactions" id="transactions">
|
||||
<input type="submit">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
<td>{{ account.source.name }}</td>
|
||||
<td>{{ account.last_updated }}</td>
|
||||
<td>
|
||||
{% include 'update_button.html' %}
|
||||
<form method="POST" action="/update/">
|
||||
<input type="hidden" name="account_uuid" value="{{ account.uuid }}"></input>
|
||||
<input type="submit" value="Update" class="btn btn-primary"></input>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block body %}
|
||||
<h1>You hit an error</h1>
|
||||
<p>{{ error }}</p>
|
||||
{% endblock %}
|
|
@ -1,4 +0,0 @@
|
|||
<form method="POST" action="/update/">
|
||||
<input type="hidden" name="account_uuid" value="{{ account.uuid }}"></input>
|
||||
<input type="submit" value="Update" class="btn btn-primary"></input>
|
||||
</form>
|
52
test.py
52
test.py
|
@ -1,52 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import requests
|
||||
|
||||
headers = {'Content-Type': 'application/x-ofx'}
|
||||
payload = (
|
||||
"OFXHEADER:100\r\n"
|
||||
"DATA:OFXSGML\r\n"
|
||||
"VERSION:102\r\n"
|
||||
"SECURITY:NONE\r\n"
|
||||
"ENCODING:USASCII\r\n"
|
||||
"CHARSET:1252\r\n"
|
||||
"COMPRESSION:NONE\r\n"
|
||||
"OLDFILEUID:NONE\r\n"
|
||||
"NEWFILEUID:NONE\r\n"
|
||||
"\r\n"
|
||||
"<OFX>\r\n"
|
||||
"<SIGNONMSGSRQV1>\r\n"
|
||||
"<SONRQ>\r\n"
|
||||
"<DTCLIENT>20140427213800.000[-7:MST]\r\n"
|
||||
"<USERID>1242940\r\n"
|
||||
"<USERPASS>0732\r\n"
|
||||
"<LANGUAGE>ENG\r\n"
|
||||
"<FI>\r\n"
|
||||
"<ORG>America First Credit Union\r\n"
|
||||
"<FID>54324\r\n"
|
||||
"</FI>\r\n"
|
||||
"<APPID>QWIN\r\n"
|
||||
"<APPVER>1200\r\n"
|
||||
"</SONRQ>\r\n"
|
||||
"</SIGNONMSGSRQV1>\r\n"
|
||||
"<BANKMSGSRQV1>\r\n"
|
||||
"<STMTTRNRQ>\r\n"
|
||||
"<TRNUID>00000000\r\n"
|
||||
"<STMTRQ>\r\n"
|
||||
"<BANKACCTFROM>\r\n"
|
||||
"<BANKID>324377516\r\n"
|
||||
"<ACCTID>124294-0.9:CHK\r\n"
|
||||
"<ACCTTYPE>CHECKING\r\n"
|
||||
"</BANKACCTFROM>\r\n"
|
||||
"<INCTRAN>\r\n"
|
||||
"<DTSTART>20160101\r\n"
|
||||
"<INCLUDE>Y\r\n"
|
||||
"</INCTRAN>\r\n"
|
||||
"</STMTRQ>\r\n"
|
||||
"</STMTTRNRQ>\r\n"
|
||||
"</BANKMSGSRQV1>\r\n"
|
||||
"</OFX>\r\n")
|
||||
#print(payload)
|
||||
response = requests.post('https://ofx.americafirst.com/', data=payload, headers=headers)
|
||||
print(response.status_code)
|
||||
print(response.headers)
|
||||
print(response.text)
|
|
@ -1,9 +0,0 @@
|
|||
import ofxparse
|
||||
import codecs
|
||||
import io
|
||||
|
||||
with open('tests/files/transactions-2.ofx', 'rb') as f:
|
||||
ofx = ofxparse.OfxParser.parse(f)
|
||||
|
||||
import pdb;pdb.set_trace()
|
||||
print(ofx)
|
|
@ -1,182 +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>20160810225540.951
|
||||
<LANGUAGE>ENG
|
||||
<DTPROFUP>20050531060000.000
|
||||
<FI>
|
||||
<ORG>AFCU
|
||||
<FID>1001
|
||||
</FI>
|
||||
<INTU.BID>54324
|
||||
<INTU.USERID>1234567
|
||||
</SONRS>
|
||||
</SIGNONMSGSRSV1>
|
||||
<BANKMSGSRSV1>
|
||||
<STMTTRNRS>
|
||||
<TRNUID>0
|
||||
<STATUS>
|
||||
<CODE>0
|
||||
<SEVERITY>INFO
|
||||
</STATUS>
|
||||
<STMTRS>
|
||||
<CURDEF>USD
|
||||
<BANKACCTFROM>
|
||||
<BANKID>324377516
|
||||
<ACCTID>1234567~9
|
||||
<ACCTTYPE>CHECKING
|
||||
</BANKACCTFROM>
|
||||
<BANKTRANLIST>
|
||||
<DTSTART>20160801060000.000
|
||||
<DTEND>20160810060000.000
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CREDIT
|
||||
<DTPOSTED>20160802120000.000
|
||||
<TRNAMT>8410.20
|
||||
<FITID>0006882
|
||||
<NAME>AUTOMATIC DEPOSIT, AUTHENTISE IN
|
||||
<MEMO>AUTOMATIC DEPOSIT, AUTHENTISE INC DIRECT DEP PPD
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160802120000.000
|
||||
<TRNAMT>-95.00
|
||||
<FITID>0006883
|
||||
<NAME>AUTOMATIC WITHDRAWAL, AMERICA FI
|
||||
<MEMO>AUTOMATIC WITHDRAWAL, AMERICA FIRST EXTERNAL TRANSFER, CHRISTINE RIBBLE WEB
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160802120000.000
|
||||
<TRNAMT>-200.00
|
||||
<FITID>0006884
|
||||
<NAME>AUTOMATIC WITHDRAWAL, UESP INVES
|
||||
<MEMO>AUTOMATIC WITHDRAWAL, UESP INVESTMENT WEB(R )
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160802120000.000
|
||||
<TRNAMT>-1943.73
|
||||
<FITID>0006885
|
||||
<NAME>AUTOMATIC WITHDRAWAL, SUNTRUST M
|
||||
<MEMO>AUTOMATIC WITHDRAWAL, SUNTRUST MORTG MTG PMTS WEB(R )
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160803120000.000
|
||||
<TRNAMT>-90.00
|
||||
<FITID>0006886
|
||||
<NAME>ONLINE BANKING FUNDS TRANSFER TO
|
||||
<MEMO>ONLINE BANKING FUNDS TRANSFER TO SHARE ACCOUNT:XXXXXX304-7.9 LUCIANA CIALABRINI
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CREDIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>519.68
|
||||
<FITID>0006887
|
||||
<NAME>ONLINE CHECK DEPOSIT
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>-97.00
|
||||
<FITID>0006888
|
||||
<NAME>FUNDS TRANSFER TO SHARE ACCOUNT:
|
||||
<MEMO>FUNDS TRANSFER TO SHARE ACCOUNT:XXXXXX304-7.9 LUCIANA CIALABRINI
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>-100.00
|
||||
<FITID>0006889
|
||||
<NAME>FUNDS TRANSFER TO SHARE ACCOUNT:
|
||||
<MEMO>FUNDS TRANSFER TO SHARE ACCOUNT:XXXXXX304-7.1 LUCIANA CIALABRINI
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>-519.68
|
||||
<FITID>0006890
|
||||
<NAME>ONLINE CHECK DEPOSIT
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CREDIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>519.68
|
||||
<FITID>0006891
|
||||
<NAME>ONLINE CHECK DEPOSIT
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160805120000.000
|
||||
<TRNAMT>-5097.73
|
||||
<FITID>0006892
|
||||
<NAME>AUTOMATIC WITHDRAWAL, DISCOVER E
|
||||
<MEMO>AUTOMATIC WITHDRAWAL, DISCOVER E-PAYMENT WEB(S )
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160806120000.000
|
||||
<TRNAMT>-300.00
|
||||
<FITID>0006893
|
||||
<NAME>WITHDRAWAL
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160808120000.000
|
||||
<TRNAMT>-211.00
|
||||
<FITID>0006894
|
||||
<NAME>ONLINE BANKING FUNDS TRANSFER TO
|
||||
<MEMO>ONLINE BANKING FUNDS TRANSFER TO SHARE ACCOUNT:XXXXXX304-7.9 LUCIANA CIALABRINI
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT
|
||||
<DTPOSTED>20160808120000.000
|
||||
<TRNAMT>-293.00
|
||||
<FITID>0006895
|
||||
<NAME>AUTOMATIC WITHDRAWAL, CHALLENGER
|
||||
<MEMO>AUTOMATIC WITHDRAWAL, CHALLENGER SCHOOTUITION PPD
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CHECK
|
||||
<DTPOSTED>20160809120000.000
|
||||
<TRNAMT>-200.00
|
||||
<FITID>0006896
|
||||
<CHECKNUM>191
|
||||
<NAME>CHECK # 191
|
||||
</STMTTRN>
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CHECK
|
||||
<DTPOSTED>20160810120000.000
|
||||
<TRNAMT>-270.00
|
||||
<FITID>0006897
|
||||
<CHECKNUM>192
|
||||
<NAME>CHECK # 192
|
||||
</STMTTRN>
|
||||
</BANKTRANLIST>
|
||||
<LEDGERBAL>
|
||||
<BALAMT>26882.49
|
||||
<DTASOF>20160810225540.951
|
||||
</LEDGERBAL>
|
||||
<AVAILBAL>
|
||||
<BALAMT>26882.49
|
||||
<DTASOF>20160810225540.951
|
||||
</AVAILBAL>
|
||||
</STMTRS>
|
||||
</STMTTRNRS>
|
||||
</BANKMSGSRSV1>
|
||||
</OFX>
|
|
@ -1,29 +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>20160810225540.951
|
||||
<LANGUAGE>ENG
|
||||
<DTPROFUP>20050531060000.000
|
||||
<FI>
|
||||
<ORG>AFCU
|
||||
<FID>1001
|
||||
</FI>
|
||||
<INTU.BID>54324
|
||||
<INTU.USERID>1242940
|
||||
</SONRS>
|
||||
</SIGNONMSGSRSV1>
|
||||
</OFX>
|
83
tests/test_ofx.py
Normal file
83
tests/test_ofx.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
import datetime
|
||||
|
||||
import vanth.ofx
|
||||
|
||||
|
||||
def MST():
|
||||
return datetime.timezone(datetime.timedelta(hours=-7), 'MST')
|
||||
|
||||
def MDT():
|
||||
return datetime.timezone(datetime.timedelta(hours=-6), 'MDT')
|
||||
|
||||
def test_query_transactions(mocker):
|
||||
institution = {
|
||||
'bankid' : "1234567",
|
||||
'fid' : "12345",
|
||||
'name' : "AFCU",
|
||||
}
|
||||
account = {
|
||||
"account_id" : "123456-0.9:CHK",
|
||||
"user_id" : "123456789",
|
||||
"password" : "1234",
|
||||
"type" : "checking",
|
||||
}
|
||||
with mocker.patch('vanth.ofx.now', return_value='20160102030405.000[-7:MST]'):
|
||||
results = vanth.ofx.query_transactions(institution, account, start=datetime.date(2016, 1, 2))
|
||||
with open('tests/files/query_transactions.ofx', 'rb') as f:
|
||||
expected = f.read().decode('utf-8')
|
||||
assert results == expected
|
||||
|
||||
def test_parse():
|
||||
with open('tests/files/transactions.ofx', 'rb') as f:
|
||||
transactions = f.read().decode('utf-8')
|
||||
document = vanth.ofx.parse(transactions)
|
||||
assert document.header == {
|
||||
'CHARSET' : '1252',
|
||||
'COMPRESSION' : 'NONE',
|
||||
'DATA' : 'OFXSGML',
|
||||
'ENCODING' : 'USASCII',
|
||||
'NEWFILEUID' : 'NONE',
|
||||
'OFXHEADER' : '100',
|
||||
'OLDFILEUID' : 'NONE',
|
||||
'SECURITY' : 'NONE',
|
||||
'VERSION' : '102'
|
||||
}
|
||||
assert document.body.status.code == '0'
|
||||
assert document.body.status.severity == 'INFO'
|
||||
assert document.body.status.message == 'The operation succeeded.'
|
||||
assert document.body.statement.status.code == '0'
|
||||
assert document.body.statement.status.severity == 'INFO'
|
||||
assert document.body.statement.status.message is None
|
||||
assert document.body.statement.transactions.currency == 'USD'
|
||||
assert document.body.statement.transactions.account.accountid == '123456-0.9:CHK'
|
||||
assert document.body.statement.transactions.account.bankid == '324377516'
|
||||
assert document.body.statement.transactions.account.type == 'CHECKING'
|
||||
assert document.body.statement.transactions.start == datetime.datetime(2015, 12, 31, 17, 0, tzinfo=MST())
|
||||
assert document.body.statement.transactions.end == datetime.datetime(2016, 6, 22, 11, 12, 42, tzinfo=MDT())
|
||||
expected_items = [{
|
||||
'amount' : -50.19,
|
||||
'available' : datetime.datetime(2015, 12, 31, 12),
|
||||
'id' : '0006547',
|
||||
'memo' : 'POINT OF SALE PURCHASE #0006547',
|
||||
'name' : 'UT LEHI COSTCO WHSE #0733',
|
||||
'posted' : datetime.datetime(2015, 12, 31, 12),
|
||||
'type' : 'POS',
|
||||
},{
|
||||
'amount' : -79.64,
|
||||
'available' : datetime.datetime(2015, 12, 31, 12),
|
||||
'id' : '0006548',
|
||||
'memo' : '#0006548',
|
||||
'name' : 'Payment to PACIFICORP ONLIN',
|
||||
'posted' : datetime.datetime(2015, 12, 31, 12),
|
||||
'type' : 'PAYMENT',
|
||||
},{
|
||||
'amount' : 0.84,
|
||||
'available' : datetime.datetime(2015, 12, 31, 12),
|
||||
'id' : '0006549',
|
||||
'memo' : 'ANNUAL PERCENTAGE YIELD EARNED IS .05% #0006549',
|
||||
'name' : 'DIVIDEND FOR 12/01/15 - 12/31/1',
|
||||
'posted' : datetime.datetime(2015, 12, 31, 12),
|
||||
'type' : 'INT',
|
||||
}]
|
||||
items = [dict(item) for item in document.body.statement.transactions.items]
|
||||
assert items == expected_items
|
17
tests/test_sgml.py
Normal file
17
tests/test_sgml.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import vanth.sgml
|
||||
|
||||
|
||||
def child_values(node):
|
||||
return [(child.name, child.value) for child in node.children]
|
||||
|
||||
def test_siblings():
|
||||
result = vanth.sgml.parse("<A><B><C>1<D>2<E>3</B></A>")
|
||||
assert result.name == 'A'
|
||||
assert child_values(result['B']) == [('C', '1'), ('D', '2'), ('E', '3')]
|
||||
|
||||
def test_closing():
|
||||
result = vanth.sgml.parse("<A><B><C>1</B><D><E>2</D></A>")
|
||||
assert result.name == 'A'
|
||||
assert child_values(result) == [('B', ''), ('D', '')]
|
||||
assert child_values(result['B']) == [('C', '1')]
|
||||
assert child_values(result['D']) == [('E', '2')]
|
File diff suppressed because it is too large
Load diff
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<institution id="533">
|
||||
<name>America First Credit Union</name>
|
||||
<fid>54324</fid>
|
||||
<org>America First Credit Union</org>
|
||||
<url>https://ofx.americafirst.com</url>
|
||||
<ofxfail>0</ofxfail>
|
||||
<sslfail>0</sslfail>
|
||||
<lastofxvalidation>2016-05-18 01:02:37</lastofxvalidation>
|
||||
<lastsslvalidation>2016-05-18 01:04:01</lastsslvalidation>
|
||||
<profile addr1="PO Box 9199" city="Ogden" state="UT" postalcode="84409" country="USA" csphone="800.999.3961" tsphone="866.224.2158" url="http://www.americafirst.com" email="support@americafirst.com" signonmsgset="true" bankmsgset="true" creditcardmsgset="true"/>
|
||||
</institution>
|
|
@ -1,43 +0,0 @@
|
|||
OFXHEADER:100
|
||||
DATA:OFXSGML
|
||||
VERSION:102
|
||||
SECURITY:NONE
|
||||
ENCODING:USASCII
|
||||
CHARSET:1252
|
||||
COMPRESSION:NONE
|
||||
OLDFILEUID:NONE
|
||||
NEWFILEUID:NONE
|
||||
|
||||
<OFX>
|
||||
<SIGNONMSGSRQV1>
|
||||
<SONRQ>
|
||||
<DTCLIENT>20160621180047.000[-7:MST]
|
||||
<USERID>1242940
|
||||
<USERPASS>0732
|
||||
<LANGUAGE>ENG
|
||||
<FI>
|
||||
<ORG>America First Credit Union
|
||||
<FID>54324
|
||||
</FI>
|
||||
<APPID>QWIN
|
||||
<APPVER>1200
|
||||
</SONRQ>
|
||||
</SIGNONMSGSRQV1>
|
||||
<BANKMSGSRQV1>
|
||||
<STMTTRNRQ>
|
||||
<TRNUID>00000000
|
||||
<STMTRQ>
|
||||
<BANKACCTFROM>
|
||||
<BANKID>324377516
|
||||
<ACCTID>124294-0.9:CHK
|
||||
<ACCTTYPE>CHECKING
|
||||
</BANKACCTFROM>
|
||||
<INCTRAN>
|
||||
<DTSTART>20140101
|
||||
<INCLUDE>Y
|
||||
</INCTRAN>
|
||||
</STMTRQ>
|
||||
</STMTTRNRQ>
|
||||
</BANKMSGSRQV1>
|
||||
</OFX>
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
OFXHEADER:100
|
||||
DATA:OFXSGML
|
||||
VERSION:102
|
||||
SECURITY:NONE
|
||||
ENCODING:USASCII
|
||||
CHARSET:1252
|
||||
COMPRESSION:NONE
|
||||
OLDFILEUID:NONE
|
||||
NEWFILEUID:NONE
|
||||
|
||||
<OFX>
|
||||
<SIGNONMSGSRQV1>
|
||||
<SONRQ>
|
||||
<DTCLIENT>20140427213800.000[-7:MST]
|
||||
<USERID>1242940
|
||||
<USERPASS>0732
|
||||
<LANGUAGE>ENG
|
||||
<FI>
|
||||
<ORG>America First Credit Union
|
||||
<FID>54324
|
||||
</FI>
|
||||
<APPID>QWIN
|
||||
<APPVER>1200
|
||||
</SONRQ>
|
||||
</SIGNONMSGSRQV1>
|
||||
<BANKMSGSRQV1>
|
||||
<STMTTRNRQ>
|
||||
<TRNUID>00000000
|
||||
<STMTRQ>
|
||||
<BANKACCTFROM>
|
||||
<BANKID>324377516
|
||||
<ACCTID>124294-0.9:CHK
|
||||
<ACCTTYPE>CHECKING
|
||||
</BANKACCTFROM>
|
||||
<INCTRAN>
|
||||
<DTSTART>20160101
|
||||
<INCLUDE>Y
|
||||
</INCTRAN>
|
||||
</STMTRQ>
|
||||
</STMTTRNRQ>
|
||||
</BANKMSGSRQV1>
|
||||
</OFX>
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<institutionlist>
|
||||
<institutionid name="121 Financial Credit Union" id="666"/>
|
||||
<institutionid name="1st Advantage FCU" id="542"/>
|
||||
<institutionid name="5 Star Bank" id="774"/>
|
||||
<institutionid name="66 Federal Credit Union" id="553"/>
|
||||
<institutionid name="A. G. Edwards and Sons, Inc." id="491"/>
|
||||
<institutionid name="Abbott Laboratories Employee CU" id="667"/>
|
||||
<institutionid name="ABNB Federal Credit Union" id="530"/>
|
||||
<institutionid name="Achieva Credit Union" id="668"/>
|
||||
<institutionid name="Addison Avenue Federal Credit Union" id="550"/>
|
||||
<institutionid name="Advantis Credit Union" id="782"/>
|
||||
<institutionid name="Affinity Plus Federal Credit Union" id="732"/>
|
||||
<institutionid name="Affinity Plus Federal Credit Union-New" id="810"/>
|
||||
<institutionid name="AIM Investments" id="497"/>
|
||||
<institutionid name="Alaska Air Visa (Bank of America)" id="796"/>
|
||||
<institutionid name="Allegiance Credit Union" id="531"/>
|
||||
<institutionid name="Alpine Banks of Colorado" id="643"/>
|
||||
<institutionid name="AltaOne" id="772"/>
|
||||
<institutionid name="AltaOne Federal Credit Union" id="490"/>
|
||||
<institutionid name="AmegyBank" id="786"/>
|
||||
<institutionid name="America First Credit Union" id="533"/>
|
||||
<institutionid name="American Century Investments" id="739"/>
|
||||
<institutionid name="American Express Card" id="424"/>
|
||||
<institutionid name="American Funds" id="590"/>
|
||||
<institutionid name="American National Bank" id="669"/>
|
||||
<institutionid name="Ameriprise Financial Services, Inc." id="489"/>
|
||||
<institutionid name="Amplify Federal Credit Union" id="788"/>
|
||||
<institutionid name="Andrews Federal Credit Union" id="670"/>
|
||||
<institutionid name="APCO EMPLOYEES CREDIT UNION" id="521"/>
|
||||
<institutionid name="Apple FCU" id="746"/>
|
||||
<institutionid name="Ariel Mutual Funds" id="566"/>
|
||||
<institutionid name="Arizona Federal Credit Union" id="637"/>
|
||||
<institutionid name="Arizona State Credit Union" id="785"/>
|
||||
<institutionid name="Ascentra Credit Union" id="423"/>
|
||||
<institutionid name="AT&T Universal Card" id="427"/>
|
||||
<institutionid name="AXA Equitable" id="763"/>
|
||||
<institutionid name="B-M S Federal Credit Union" id="505"/>
|
||||
<institutionid name="BancFirst" id="644"/>
|
||||
<institutionid name="Bancorpsouth" id="803"/>
|
||||
<institutionid name="Bank of America" id="639"/>
|
||||
<institutionid name="Bank of America (All except CA, WA,&ID)" id="472"/>
|
||||
<institutionid name="Bank of America (California)" id="635"/>
|
||||
<institutionid name="Bank of America (Formerly Fleet)" id="674"/>
|
||||
<institutionid name="Bank of America - 5959" id="765"/>
|
||||
<institutionid name="Bank of America - access.ofx" id="790"/>
|
||||
<institutionid name="Bank Of America(All except CA,WA,&ID " id="661"/>
|
||||
<institutionid name="Bank of George" id="733"/>
|
||||
<institutionid name="Bank of Internet, USA" id="787"/>
|
||||
<institutionid name="Bank of Stockton" id="429"/>
|
||||
<institutionid name="Bank of Tampa, The" id="522"/>
|
||||
<institutionid name="Bank of the Cascades" id="430"/>
|
||||
<institutionid name="Bank of the West" id="656"/>
|
||||
<institutionid name="Bank One" id="428"/>
|
||||
<institutionid name="Bank One (Chicago)" id="672"/>
|
||||
<institutionid name="Bank One (Michigan and Florida)" id="673"/>
|
||||
<institutionid name="Bank-Fund Staff FCU" id="520"/>
|
||||
<institutionid name="BankBoston PC Banking" id="675"/>
|
||||
<institutionid name="bankfinancial" id="762"/>
|
||||
<institutionid name="Baton Rouge City Parish Emp FCU" id="576"/>
|
||||
<institutionid name="BB&T" id="475"/>
|
||||
<institutionid name="Belmont Savings Bank" id="775"/>
|
||||
<institutionid name="Bernstein Global Wealth Mgmt" id="605"/>
|
||||
<institutionid name="Beverly Co-Operative Bank" id="676"/>
|
||||
<institutionid name="Billings Federal Credit Union" id="603"/>
|
||||
<institutionid name="Boeing Employees Credit Union" id="647"/>
|
||||
<institutionid name="Bofi federal bank" id="798"/>
|
||||
<institutionid name="Bossier Federal Credit Union" id="611"/>
|
||||
<institutionid name="California Bank&Trust" id="487"/>
|
||||
<institutionid name="Cambridge Portuguese Credit Union" id="677"/>
|
||||
<institutionid name="Cambridge Savings Bank" id="760"/>
|
||||
<institutionid name="Camino FCU" id="580"/>
|
||||
<institutionid name="Campus USA Credit Union" id="546"/>
|
||||
<institutionid name="Capital One" id="628"/>
|
||||
<institutionid name="Capital One 360" id="783"/>
|
||||
<institutionid name="Capital One Bank" id="631"/>
|
||||
<institutionid name="Capital One Bank (after 12-15-13)" id="802"/>
|
||||
<institutionid name="Capital One Bank - 2" id="648"/>
|
||||
<institutionid name="Capitol Federal Savings Bank" id="789"/>
|
||||
<institutionid name="Cedar Point Federal Credit Union" id="523"/>
|
||||
<institutionid name="CenterState Bank" id="773"/>
|
||||
<institutionid name="Centra Credit Union" id="431"/>
|
||||
<institutionid name="Centra Credit Union2" id="662"/>
|
||||
<institutionid name="Central Bank Utah" id="820"/>
|
||||
<institutionid name="Central Florida Educators FCU" id="486"/>
|
||||
<institutionid name="Central Maine FCU" id="543"/>
|
||||
<institutionid name="Centura Bank" id="432"/>
|
||||
<institutionid name="Century Federal Credit Union" id="529"/>
|
||||
<institutionid name="Charles Schwab Bank, N.A." id="578"/>
|
||||
<institutionid name="Charles Schwab Retirement" id="729"/>
|
||||
<institutionid name="Charles Schwab Retirement Plan Services" id="730"/>
|
||||
<institutionid name="Charles Schwab&Co., INC" id="433"/>
|
||||
<institutionid name="Chase (credit card) " id="636"/>
|
||||
<institutionid name="Chemical Bank" id="747"/>
|
||||
<institutionid name="Chesterfield Federal Credit Union" id="545"/>
|
||||
<institutionid name="Chicago Patrolmens FCU" id="517"/>
|
||||
<institutionid name="Citadel FCU" id="477"/>
|
||||
<institutionid name="Citi Credit Card" id="629"/>
|
||||
<institutionid name="Citi Personal Wealth Management" id="671"/>
|
||||
<institutionid name="Citibank" id="678"/>
|
||||
<institutionid name="Citizens Bank" id="664"/>
|
||||
<institutionid name="Citizens Bank - Business" id="528"/>
|
||||
<institutionid name="Citizens Bank - Consumer" id="527"/>
|
||||
<institutionid name="Citizens National Bank" id="768"/>
|
||||
<institutionid name="Clearview Federal Credit Union" id="478"/>
|
||||
<institutionid name="Collegedale Credit Union" id="496"/>
|
||||
<institutionid name="Colonial Bank" id="436"/>
|
||||
<institutionid name="Columbia Credit Union" id="541"/>
|
||||
<institutionid name="Comerica Bank" id="437"/>
|
||||
<institutionid name="Commerce Bank" id="640"/>
|
||||
<institutionid name="Commerce Bank NJ, PA, NY&DE" id="438"/>
|
||||
<institutionid name="Commerce Bank, NA" id="439"/>
|
||||
<institutionid name="Commercial Federal Bank" id="440"/>
|
||||
<institutionid name="Community 1st Credit Union" id="738"/>
|
||||
<institutionid name="Community Bank, N.A." id="679"/>
|
||||
<institutionid name="Community First Credit Union" id="592"/>
|
||||
<institutionid name="Community Resource Bank" id="600"/>
|
||||
<institutionid name="CompassPC" id="824"/>
|
||||
<institutionid name="COMSTAR FCU" id="441"/>
|
||||
<institutionid name="Consumers Credit Union" id="680"/>
|
||||
<institutionid name="Continental Federal Credit Union" id="555"/>
|
||||
<institutionid name="CPM Federal Credit Union" id="681"/>
|
||||
<institutionid name="Credit Suisse Securities USA LLC" id="753"/>
|
||||
<institutionid name="Credit Union 1 - IL" id="610"/>
|
||||
<institutionid name="Credit Union ONE" id="618"/>
|
||||
<institutionid name="Cyprus Federal Credit Union" id="569"/>
|
||||
<institutionid name="D. A. Davidson" id="805"/>
|
||||
<institutionid name="DATCU" id="682"/>
|
||||
<institutionid name="Day Air Credit Union" id="585"/>
|
||||
<institutionid name="Delta Community Credit Union" id="573"/>
|
||||
<institutionid name="Denali Alaskan FCU" id="443"/>
|
||||
<institutionid name="Denver Community Federal Credit Union" id="683"/>
|
||||
<institutionid name="Desert Schools Federal Credit Union" id="645"/>
|
||||
<institutionid name="Discover Bank" id="726"/>
|
||||
<institutionid name="Discover Card" id="444"/>
|
||||
<institutionid name="Discover Platinum" id="684"/>
|
||||
<institutionid name="Dodge&Cox Funds" id="621"/>
|
||||
<institutionid name="Dominion Credit Union" id="561"/>
|
||||
<institutionid name="Dreyfus" id="445"/>
|
||||
<institutionid name="Dupaco Community Credit Union" id="519"/>
|
||||
<institutionid name="DuPont Community Credit Union" id="485"/>
|
||||
<institutionid name="E*TRADE" id="446"/>
|
||||
<institutionid name="EAB" id="685"/>
|
||||
<institutionid name="Eastern Bank" id="447"/>
|
||||
<institutionid name="EDS Credit Union" id="448"/>
|
||||
<institutionid name="Educational Employees CU Fresno" id="492"/>
|
||||
<institutionid name="Edward Jones" id="608"/>
|
||||
<institutionid name="Elevations Credit Union" id="727"/>
|
||||
<institutionid name="Elevations Credit Union IB WC-DC" id="752"/>
|
||||
<institutionid name="Envision Credit Union" id="540"/>
|
||||
<institutionid name="FAA Credit Union" id="686"/>
|
||||
<institutionid name="FAA Technical Center FCU" id="583"/>
|
||||
<institutionid name="Fairwinds Credit Union" id="687"/>
|
||||
<institutionid name="Fall River Municipal CU" id="559"/>
|
||||
<institutionid name="FedChoice FCU" id="688"/>
|
||||
<institutionid name="Fidelity Investments" id="449"/>
|
||||
<institutionid name="Fidelity NetBenefits" id="558"/>
|
||||
<institutionid name="Fifth Third Bancorp" id="450"/>
|
||||
<institutionid name="Finance Center FCU (IN)" id="535"/>
|
||||
<institutionid name="Financial Center CU" id="548"/>
|
||||
<institutionid name="First Alliance Credit Union" id="602"/>
|
||||
<institutionid name="First Citizens" id="690"/>
|
||||
<institutionid name="First Citizens Bank - NC, VA, WV" id="480"/>
|
||||
<institutionid name="First Clearing, LLC" id="689"/>
|
||||
<institutionid name="First Command Bank" id="766"/>
|
||||
<institutionid name="First Commonwealth FCU" id="488"/>
|
||||
<institutionid name="First Community FCU" id="515"/>
|
||||
<institutionid name="First Florida Credit Union" id="612"/>
|
||||
<institutionid name="First Hawaiian Bank" id="691"/>
|
||||
<institutionid name="First Internet Bank of Indiana" id="642"/>
|
||||
<institutionid name="First Interstate Bank" id="693"/>
|
||||
<institutionid name="First National Bank (Texas)" id="655"/>
|
||||
<institutionid name="First National Bank of St. Louis" id="692"/>
|
||||
<institutionid name="First Republic Bank" id="770"/>
|
||||
<institutionid name="First Southwest Company" id="620"/>
|
||||
<institutionid name="First Tech Credit Union" id="451"/>
|
||||
<institutionid name="First Tech Federal Credit Union" id="731"/>
|
||||
<institutionid name="First Tennessee" id="795"/>
|
||||
<institutionid name="Firstar" id="494"/>
|
||||
<institutionid name="FirstBank of Colorado" id="554"/>
|
||||
<institutionid name="FivePoint Credit Union" id="599"/>
|
||||
<institutionid name="Flagstar Bank" id="784"/>
|
||||
<institutionid name="Florida Telco CU" id="484"/>
|
||||
<institutionid name="Fort Knox Federal Credit Union" id="536"/>
|
||||
<institutionid name="Fort Stewart GeorgiaFCU" id="506"/>
|
||||
<institutionid name="Franklin Templeton Investments" id="734"/>
|
||||
<institutionid name="Fremont Bank" id="556"/>
|
||||
<institutionid name="GCS Federal Credit Union" id="498"/>
|
||||
<institutionid name="Goldman Sachs" id="694"/>
|
||||
<institutionid name="Haverhill Bank" id="757"/>
|
||||
<institutionid name="Hawaii State FCU" id="588"/>
|
||||
<institutionid name="Hawaiian Tel Federal Credit Union" id="549"/>
|
||||
<institutionid name="Hawthorne Credit Union" id="493"/>
|
||||
<institutionid name="Hewitt Associates LLC" id="572"/>
|
||||
<institutionid name="HFS Federal Credit Union" id="562"/>
|
||||
<institutionid name="Home Federal Savings Bank(MN/IA)" id="594"/>
|
||||
<institutionid name="Hudson Valley FCU" id="695"/>
|
||||
<institutionid name="Hughes Federal Credit Union" id="745"/>
|
||||
<institutionid name="Huntington National Bank" id="574"/>
|
||||
<institutionid name="IBM Southeast Employees Federal Credit Union" id="696"/>
|
||||
<institutionid name="Iinvestor360" id="792"/>
|
||||
<institutionid name="ING DIRECT" id="658"/>
|
||||
<institutionid name="ING DIRECT (Canada)" id="421"/>
|
||||
<institutionid name="ING Institutional Plan Services " id="735"/>
|
||||
<institutionid name="Insight CU" id="697"/>
|
||||
<institutionid name="Institution For Savings" id="816"/>
|
||||
<institutionid name="International Bank of Commerce" id="743"/>
|
||||
<institutionid name="IronStone Bank" id="563"/>
|
||||
<institutionid name="J.P. Morgan" id="700"/>
|
||||
<institutionid name="J.P. Morgan Clearing Corp." id="701"/>
|
||||
<institutionid name="J.P. Morgan Private Banking" id="740"/>
|
||||
<institutionid name="Janney Montgomery Scott LLC" id="698"/>
|
||||
<institutionid name="Janus" id="615"/>
|
||||
<institutionid name="JPMorgan Chase Bank" id="435"/>
|
||||
<institutionid name="JPMorgan Chase Bank (Texas)" id="434"/>
|
||||
<institutionid name="JPMorgan Retirement Plan Services" id="617"/>
|
||||
<institutionid name="JSC Federal Credit Union" id="699"/>
|
||||
<institutionid name="Kennedy Space Center FCU" id="501"/>
|
||||
<institutionid name="KeyBank" id="453"/>
|
||||
<institutionid name="Kinecta Federal Credit Union" id="646"/>
|
||||
<institutionid name="Kirtland Federal Credit Union" id="544"/>
|
||||
<institutionid name="Kitsap Community Credit Union" id="728"/>
|
||||
<institutionid name="La Banque Postale" id="813"/>
|
||||
<institutionid name="Landings Credit Union" id="822"/>
|
||||
<institutionid name="Las Colinas FCU" id="524"/>
|
||||
<institutionid name="LaSalle Bank Midwest" id="455"/>
|
||||
<institutionid name="LaSalle Bank NA" id="474"/>
|
||||
<institutionid name="Local Government Federal Credit Union" id="748"/>
|
||||
<institutionid name="Los Alamos National Bank" id="476"/>
|
||||
<institutionid name="M & T Bank" id="702"/>
|
||||
<institutionid name="Mainline National Bank" id="663"/>
|
||||
<institutionid name="Marquette Banks" id="703"/>
|
||||
<institutionid name="Mayo Employees Federal Credit Union" id="598"/>
|
||||
<institutionid name="McCoy Federal Credit Union" id="525"/>
|
||||
<institutionid name="Mellon Bank" id="454"/>
|
||||
<institutionid name="Mercantile Brokerage Services" id="461"/>
|
||||
<institutionid name="Mercer" id="704"/>
|
||||
<institutionid name="Merck Sharp&Dohme FCU" id="609"/>
|
||||
<institutionid name="Merrill Lynch Online Payment" id="705"/>
|
||||
<institutionid name="Merrill Lynch&Co., Inc." id="510"/>
|
||||
<institutionid name="Metro Bank" id="654"/>
|
||||
<institutionid name="Michigan State University Federal CU" id="649"/>
|
||||
<institutionid name="Mission Federal Credit Union" id="758"/>
|
||||
<institutionid name="Missoula Federal Credit Union" id="706"/>
|
||||
<institutionid name="Monterey Credit Union" id="804"/>
|
||||
<institutionid name="Morgan Stanley (Smith Barney)" id="707"/>
|
||||
<institutionid name="Morgan Stanley ClientServ" id="500"/>
|
||||
<institutionid name="Morgan Stanley ClientServ - Quicken Win Format" id="806"/>
|
||||
<institutionid name="Motorola Employees Credit Union" id="534"/>
|
||||
<institutionid name="Mountain America Credit Union" id="657"/>
|
||||
<institutionid name="MTC Federal Credit Union" id="593"/>
|
||||
<institutionid name="Municipal Employees Credit Union of Baltimore, Inc." id="584"/>
|
||||
<institutionid name="Mutual Bank" id="809"/>
|
||||
<institutionid name="myStreetscape" id="495"/>
|
||||
<institutionid name="Nantucket Bank" id="456"/>
|
||||
<institutionid name="National City" id="627"/>
|
||||
<institutionid name="National Penn Bank" id="457"/>
|
||||
<institutionid name="Navy Army Federal Credit Union" id="551"/>
|
||||
<institutionid name="NetxClient UAT" id="761"/>
|
||||
<institutionid name="Nevada Federal Credit Union" id="552"/>
|
||||
<institutionid name="Nevada State Bank - New" id="458"/>
|
||||
<institutionid name="Nevada State Bank - OLD" id="708"/>
|
||||
<institutionid name="New England Federal Credit Union" id="709"/>
|
||||
<institutionid name="North Carolina State Employees Credit Union" id="742"/>
|
||||
<institutionid name="North Carolina State Employees' Credit Union" id="632"/>
|
||||
<institutionid name="North Country FCU" id="754"/>
|
||||
<institutionid name="NorthEast Alliance FCU" id="613"/>
|
||||
<institutionid name="Northern Trust - Banking" id="481"/>
|
||||
<institutionid name="Northern Trust - Investments" id="507"/>
|
||||
<institutionid name="Northwest Community CU" id="741"/>
|
||||
<institutionid name="Norwest" id="710"/>
|
||||
<institutionid name="Novartis Federal Credit Union" id="581"/>
|
||||
<institutionid name="nuVision Financial FCU" id="821"/>
|
||||
<institutionid name="NW Preferred Federal Credit Union" id="579"/>
|
||||
<institutionid name="OCTFCU" id="587"/>
|
||||
<institutionid name="Old National Bank" id="526"/>
|
||||
<institutionid name="Oppenheimer & Co. Inc." id="711"/>
|
||||
<institutionid name="OptionsXpress, Inc" id="565"/>
|
||||
<institutionid name="Oregon College Savings Plan" id="712"/>
|
||||
<institutionid name="Oregon Community Credit Union" id="781"/>
|
||||
<institutionid name="Patelco CU" id="460"/>
|
||||
<institutionid name="Patriots Federal Credit Union" id="596"/>
|
||||
<institutionid name="Peninsula Community Federal Credit Union" id="557"/>
|
||||
<institutionid name="Pennsylvania State Employees Credit Union" id="814"/>
|
||||
<institutionid name="Penson Financial Services" id="570"/>
|
||||
<institutionid name="Picatinny Federal Credit Union" id="508"/>
|
||||
<institutionid name="PNC Bank" id="634"/>
|
||||
<institutionid name="PNC Banking Online" id="818"/>
|
||||
<institutionid name="PNC Online Banking" id="817"/>
|
||||
<institutionid name="Premier America Credit Union" id="764"/>
|
||||
<institutionid name="Premier Members FCU" id="823"/>
|
||||
<institutionid name="Prudential Retirement" id="567"/>
|
||||
<institutionid name="PSECU" id="539"/>
|
||||
<institutionid name="RaboBank America" id="744"/>
|
||||
<institutionid name="RBC Dain Rauscher" id="713"/>
|
||||
<institutionid name="Red Crown Federal Credit Union" id="504"/>
|
||||
<institutionid name="Redstone Federal Credit Union" id="633"/>
|
||||
<institutionid name="Regions Bank" id="462"/>
|
||||
<institutionid name="Reliant Community Credit Union" id="595"/>
|
||||
<institutionid name="Robert W. Baird & Co." id="714"/>
|
||||
<institutionid name="Royce&Associates" id="589"/>
|
||||
<institutionid name="SAC FEDERAL CREDIT UNION" id="509"/>
|
||||
<institutionid name="Sacramento Credit Union" id="651"/>
|
||||
<institutionid name="Safe Credit Union - OFX Beta" id="422"/>
|
||||
<institutionid name="SafeAmerica Credit Union" id="597"/>
|
||||
<institutionid name="Salt Lake City Credit Union" id="619"/>
|
||||
<institutionid name="Sandia Laboratory Federal Credit Union" id="780"/>
|
||||
<institutionid name="Santa Barbara Bank & Trust" id="659"/>
|
||||
<institutionid name="Schools Financial Credit Union" id="577"/>
|
||||
<institutionid name="Schwab Retirement Plan Services" id="750"/>
|
||||
<institutionid name="Scottrade Brokerage" id="808"/>
|
||||
<institutionid name="Scottrade, Inc." id="623"/>
|
||||
<institutionid name="Sears Card" id="715"/>
|
||||
<institutionid name="Securities America" id="641"/>
|
||||
<institutionid name="Security 1st FCU" id="601"/>
|
||||
<institutionid name="ShareBuilder" id="614"/>
|
||||
<institutionid name="Sierra Central Credit Union" id="502"/>
|
||||
<institutionid name="Signal Financial Federal Credit Union" id="518"/>
|
||||
<institutionid name="Silver State Schools CU" id="624"/>
|
||||
<institutionid name="Siouxland Federal Credit Union" id="606"/>
|
||||
<institutionid name="Smith Barney - Investments" id="625"/>
|
||||
<institutionid name="Smith Barney - Transactions" id="464"/>
|
||||
<institutionid name="Sound CU" id="793"/>
|
||||
<institutionid name="South Carolina Bank and Trust" id="755"/>
|
||||
<institutionid name="South Trust Bank" id="716"/>
|
||||
<institutionid name="Southeastern CU" id="511"/>
|
||||
<institutionid name="Southern Community Bank and Trust (SCB&T)" id="751"/>
|
||||
<institutionid name="Southwest Airlines FCU" id="465"/>
|
||||
<institutionid name="Southwest Missouri Bank" id="759"/>
|
||||
<institutionid name="Spectrum Connect/Reich&Tang" id="463"/>
|
||||
<institutionid name="St. Mary's Credit Union" id="815"/>
|
||||
<institutionid name="Standard Federal Bank" id="717"/>
|
||||
<institutionid name="Star One Credit Union" id="807"/>
|
||||
<institutionid name="Sterne Agee" id="736"/>
|
||||
<institutionid name="Summit Credit Union (WI)" id="547"/>
|
||||
<institutionid name="Suncoast Credit Union" id="811"/>
|
||||
<institutionid name="Suncoast Schools FCU" id="653"/>
|
||||
<institutionid name="SunTrust" id="442"/>
|
||||
<institutionid name="SVB" id="791"/>
|
||||
<institutionid name="T. Rowe Price" id="466"/>
|
||||
<institutionid name="Tangerine (Canada)" id="794"/>
|
||||
<institutionid name="TD Ameritrade" id="425"/>
|
||||
<institutionid name="TD Bank" id="652"/>
|
||||
<institutionid name="Technology Credit Union" id="801"/>
|
||||
<institutionid name="Technology Credit Union - CA" id="467"/>
|
||||
<institutionid name="Texans Credit Union" id="771"/>
|
||||
<institutionid name="Texas Dow Employees Credit Union" id="512"/>
|
||||
<institutionid name="Texas State Bank - McAllen" id="586"/>
|
||||
<institutionid name="The Community Bank" id="650"/>
|
||||
<institutionid name="The Golden1 Credit Union" id="778"/>
|
||||
<institutionid name="The Mechanics Bank" id="482"/>
|
||||
<institutionid name="The Queen's Federal Credit Union" id="607"/>
|
||||
<institutionid name="Think Federal Credit Union" id="538"/>
|
||||
<institutionid name="Think Mutual Bank" id="812"/>
|
||||
<institutionid name="TIAA-CREF" id="767"/>
|
||||
<institutionid name="TIAA-CREF Retirement Services" id="797"/>
|
||||
<institutionid name="Tower Federal Credit Union" id="769"/>
|
||||
<institutionid name="Tri Boro Federal Credit Union" id="571"/>
|
||||
<institutionid name="Truliant FCU" id="426"/>
|
||||
<institutionid name="U.S. First FCU" id="582"/>
|
||||
<institutionid name="UBS Financial Services Inc." id="459"/>
|
||||
<institutionid name="UMB" id="660"/>
|
||||
<institutionid name="UMB Bank" id="468"/>
|
||||
<institutionid name="Umpqua Bank" id="725"/>
|
||||
<institutionid name="Union Bank of California" id="469"/>
|
||||
<institutionid name="United California Bank" id="718"/>
|
||||
<institutionid name="United Federal CU - PowerLink" id="719"/>
|
||||
<institutionid name="United Teletech Financial" id="470"/>
|
||||
<institutionid name="UNIVERSITY & STATE EMPLOYEES CU" id="776"/>
|
||||
<institutionid name="University Credit Union" id="560"/>
|
||||
<institutionid name="University Federal Credit Union" id="513"/>
|
||||
<institutionid name="US Bank" id="471"/>
|
||||
<institutionid name="USAA Federal Savings Bank" id="483"/>
|
||||
<institutionid name="USAA Investment Mgmt Co" id="665"/>
|
||||
<institutionid name="Utah Community Credit Union" id="564"/>
|
||||
<institutionid name="UW Credit Union" id="638"/>
|
||||
<institutionid name="VALIC" id="720"/>
|
||||
<institutionid name="Van Kampen Funds, Inc." id="721"/>
|
||||
<institutionid name="Vanguard" id="799"/>
|
||||
<institutionid name="Vanguard Group" id="722"/>
|
||||
<institutionid name="Vanguard Group, The" id="479"/>
|
||||
<institutionid name="Vantage Credit Union" id="499"/>
|
||||
<institutionid name="Velocity Credit Union" id="723"/>
|
||||
<institutionid name="Virginia Educators Credit Union" id="503"/>
|
||||
<institutionid name="VISA Information Source" id="626"/>
|
||||
<institutionid name="Voya" id="819"/>
|
||||
<institutionid name="Wachovia Bank" id="537"/>
|
||||
<institutionid name="Waddell & Reed - Ivy Funds" id="724"/>
|
||||
<institutionid name="Weitz Funds" id="616"/>
|
||||
<institutionid name="Wells Fargo" id="473"/>
|
||||
<institutionid name="Wells Fargo Advantage Funds" id="591"/>
|
||||
<institutionid name="Wells Fargo Advisor" id="516"/>
|
||||
<institutionid name="Wells Fargo Advisors" id="737"/>
|
||||
<institutionid name="Wells Fargo Bank" id="749"/>
|
||||
<institutionid name="Wells Fargo Bank 2013" id="777"/>
|
||||
<institutionid name="Wells Fargo Investments, LLC" id="568"/>
|
||||
<institutionid name="Wells Fargo Trust-Investment Mgt" id="622"/>
|
||||
<institutionid name="Windward Community FCU" id="604"/>
|
||||
<institutionid name="Wings Financial" id="756"/>
|
||||
<institutionid name="Woodsboro Bank" id="779"/>
|
||||
<institutionid name="Wright Patman Congressional FCU" id="532"/>
|
||||
<institutionid name="Wright Patt CU" id="800"/>
|
||||
<institutionid name="WSECU" id="575"/>
|
||||
<institutionid name="Yakima Valley Credit Union" id="514"/>
|
||||
<institutionid name="Zions Bank" id="630"/>
|
||||
<institutionid name="zWachovia" id="452"/>
|
||||
</institutionlist>
|
|
@ -1 +0,0 @@
|
|||
None
|
|
@ -1,8 +1,6 @@
|
|||
import logging
|
||||
|
||||
import celery
|
||||
import ofxparse
|
||||
import requests.exceptions
|
||||
|
||||
import vanth.download
|
||||
import vanth.main
|
||||
|
@ -16,24 +14,13 @@ LOGGER = logging.getLogger(__name__)
|
|||
app = celery.Celery('vanth')
|
||||
app.conf.CELERY_ACCEPT_CONTENT = ['json', 'msgpack', 'yaml']
|
||||
app.conf.CELERY_TASK_SERIALIZER = 'json'
|
||||
#app.conf.CELERY_ALWAYS_EAGER = True
|
||||
app.conf.CELERY_ALWAYS_EAGER = True
|
||||
|
||||
@app.task()
|
||||
def update_account(account_uuid):
|
||||
LOGGER.debug("Updating account %s", account_uuid)
|
||||
account = vanth.platform.ofxaccount.by_uuid(account_uuid)
|
||||
source = vanth.platform.ofxsource.by_uuid(account['source']['uuid'])[0]
|
||||
try:
|
||||
document = vanth.download.transactions(source, account)
|
||||
vanth.platform.ofxrecord.ensure_exists(account, document.body.statement.transactions.items)
|
||||
vanth.platform.ofxupdate.OFXUpdate.create(ofxaccount=account_uuid)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
LOGGER.error("Failed to download account data: %s", e)
|
||||
|
||||
@app.task()
|
||||
def process_transaction_upload(account_uuid, filename):
|
||||
LOGGER.debug("Processing file %s", filename)
|
||||
account = vanth.platform.ofxaccount.by_uuid(account_uuid)
|
||||
with open(filename, 'rb') as f:
|
||||
document = ofxparse.OfxParser.parse(f)
|
||||
vanth.platform.ofxrecord.ensure_exists(account, document.account.statement.transactions)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import logging
|
||||
|
||||
import celery
|
||||
import celery.signals
|
||||
|
||||
import vanth.main
|
||||
from vanth.celery import app
|
||||
|
||||
logging.basicConfig()
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
config = vanth.main.get_config()
|
||||
vanth.main.create_db_connection(config)
|
||||
|
||||
@celery.signals.setup_logging.connect
|
||||
def on_logging(*args, **kwargs):
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
LOGGER.info("Logging has been set up")
|
|
@ -1,7 +1,6 @@
|
|||
import io
|
||||
import ofxparse
|
||||
import requests
|
||||
|
||||
import vanth.ofx
|
||||
import vanth.platform.ofxaccount
|
||||
|
||||
|
||||
|
@ -13,8 +12,6 @@ def do_all():
|
|||
|
||||
def transactions(source, account):
|
||||
body = vanth.ofx.query_transactions(source, account)
|
||||
url = 'https://ofx.americafirst.com/'
|
||||
response = requests.post(url, data=body, headers={'Content-Type': 'application/x-ofx'})
|
||||
response = requests.post('https://ofx.americafirst.com/', data=body, headers={'Content-Type': 'application/x-ofx'})
|
||||
assert response.ok, response.text
|
||||
LOGGER.debug("Response from %s: %s %d bytes", url, response.status_code, len(response.text))
|
||||
return parse(response.text)
|
||||
return vanth.ofx.parse(response.text)
|
||||
|
|
148
vanth/ofx.py
Normal file
148
vanth/ofx.py
Normal file
|
@ -0,0 +1,148 @@
|
|||
import collections
|
||||
import datetime
|
||||
import re
|
||||
|
||||
import vanth.sgml
|
||||
|
||||
Document = collections.namedtuple('Document', ['header', 'body'])
|
||||
|
||||
class Body(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.status = Status(sgml['SIGNONMSGSRSV1']['SONRS']['STATUS'])
|
||||
self.statement = TransactionStatement(sgml['BANKMSGSRSV1']['STMTTRNRS'])
|
||||
|
||||
class Status(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.code = sgml['CODE'].value
|
||||
self.severity = sgml['SEVERITY'].value
|
||||
self.message = sgml['MESSAGE'].value if sgml['MESSAGE'] else None
|
||||
|
||||
class TransactionStatement(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.trnuid = sgml['TRNUID'].value
|
||||
self.status = Status(sgml['STATUS'])
|
||||
self.transactions = TransactionList(sgml['STMTRS'])
|
||||
|
||||
class TransactionList(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.currency = sgml['CURDEF'].value
|
||||
self.account = Account(sgml['BANKACCTFROM'])
|
||||
self.start = _parse_date_with_tz(sgml['BANKTRANLIST']['DTSTART'].value)
|
||||
self.end = _parse_date_with_tz(sgml['BANKTRANLIST']['DTEND'].value)
|
||||
self.items = [Transaction(child) for child in sgml['BANKTRANLIST'].children if child.name == 'STMTTRN']
|
||||
|
||||
class Transaction(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.amount = float(sgml['TRNAMT'].value)
|
||||
self.available = _parse_date(sgml['DTAVAIL'].value)
|
||||
self.id = sgml['FITID'].value
|
||||
self.memo = sgml['MEMO'].value
|
||||
self.name = sgml['NAME'].value
|
||||
self.posted = _parse_date(sgml['DTPOSTED'].value)
|
||||
self.type = sgml['TRNTYPE'].value
|
||||
|
||||
def __iter__(self):
|
||||
return ((prop, getattr(self, prop)) for prop in ('amount', 'available', 'id', 'memo', 'name', 'posted', 'type'))
|
||||
|
||||
class Account(): # pylint:disable=too-few-public-methods
|
||||
def __init__(self, sgml):
|
||||
self.bankid = sgml['BANKID'].value
|
||||
self.accountid = sgml['ACCTID'].value
|
||||
self.type = sgml['ACCTTYPE'].value
|
||||
|
||||
def _fix_offset(offset):
|
||||
result = int(offset) * 100
|
||||
return "{:04d}".format(result) if result > 0 else "{:05d}".format(result)
|
||||
|
||||
def _parse_date(date):
|
||||
return datetime.datetime.strptime(date, "%Y%m%d%H%M%S.000")
|
||||
|
||||
def _parse_date_with_tz(date):
|
||||
match = re.match(r'(?P<datetime>\d+)\.\d+\[(?P<offset>[\d\-]+):(?P<tzname>\w+)\]', date)
|
||||
if not match:
|
||||
raise ValueError("Unable to extract datetime from {}".format(date))
|
||||
formatted = "{datetime} {offset} {tzname}".format(
|
||||
datetime = match.group('datetime'),
|
||||
offset = _fix_offset(match.group('offset')),
|
||||
tzname = match.group('tzname'),
|
||||
)
|
||||
return datetime.datetime.strptime(formatted, "%Y%m%d%H%M%S %z %Z")
|
||||
|
||||
def header():
|
||||
return "\r\n".join([
|
||||
"OFXHEADER:100",
|
||||
"DATA:OFXSGML",
|
||||
"VERSION:102",
|
||||
"SECURITY:NONE",
|
||||
"ENCODING:USASCII",
|
||||
"CHARSET:1252",
|
||||
"COMPRESSION:NONE",
|
||||
"OLDFILEUID:NONE",
|
||||
"NEWFILEUID:NONE",
|
||||
])
|
||||
|
||||
def now():
|
||||
return datetime.datetime.now().strftime("%Y%m%d%H%M%S.000[-7:MST]")
|
||||
|
||||
def signonmsg(institution, account):
|
||||
return "\r\n".join([
|
||||
"<SIGNONMSGSRQV1>",
|
||||
"<SONRQ>",
|
||||
"<DTCLIENT>{}".format(now()),
|
||||
"<USERID>{}".format(account['user_id']),
|
||||
"<USERPASS>{}".format(account['password']),
|
||||
"<LANGUAGE>ENG",
|
||||
"<FI>",
|
||||
"<ORG>{}".format(institution['name']),
|
||||
"<FID>{}".format(institution['fid']),
|
||||
"</FI>",
|
||||
"<APPID>QWIN",
|
||||
"<APPVER>1200",
|
||||
"</SONRQ>",
|
||||
"</SIGNONMSGSRQV1>",
|
||||
])
|
||||
|
||||
def bankmsg(institution, account, start):
|
||||
return "\r\n".join([
|
||||
"<BANKMSGSRQV1>",
|
||||
"<STMTTRNRQ>",
|
||||
"<TRNUID>00000000",
|
||||
"<STMTRQ>",
|
||||
"<BANKACCTFROM>",
|
||||
"<BANKID>{}".format(institution['bankid']),
|
||||
"<ACCTID>{}".format(account['account_id']),
|
||||
"<ACCTTYPE>{}".format(account['type'].upper()),
|
||||
"</BANKACCTFROM>",
|
||||
"<INCTRAN>",
|
||||
"<DTSTART>{}".format(start.strftime("%Y%m%d")),
|
||||
"<INCLUDE>Y",
|
||||
"</INCTRAN>",
|
||||
"</STMTRQ>",
|
||||
"</STMTTRNRQ>",
|
||||
"</BANKMSGSRQV1>",
|
||||
])
|
||||
|
||||
def body(institution, account, start):
|
||||
return "<OFX>\r\n" + signonmsg(institution, account) + "\r\n" + bankmsg(institution, account, start) + "\r\n</OFX>"
|
||||
|
||||
def query_transactions(institution, account, start=None):
|
||||
start = start or datetime.datetime.now() - datetime.timedelta(days=14)
|
||||
return header() + (2*"\r\n") + body(institution, account, start) + "\r\n"
|
||||
|
||||
|
||||
def _first_empty_line(lines):
|
||||
for i, line in enumerate(lines):
|
||||
if not line:
|
||||
return i
|
||||
|
||||
def _parse_header(header_lines):
|
||||
splits = [line.partition(':') for line in header_lines]
|
||||
return {k: v for k, _, v in splits}
|
||||
|
||||
def parse(content):
|
||||
lines = content.split('\r\n')
|
||||
split = _first_empty_line(lines)
|
||||
header_lines = lines[:split]
|
||||
_header = _parse_header(header_lines)
|
||||
_body = vanth.sgml.parse('\n'.join(lines[split+1:]))
|
||||
return Document(_header, Body(_body))
|
|
@ -1,12 +0,0 @@
|
|||
import xml.etree.ElementTree
|
||||
|
||||
|
||||
def parse_child(element):
|
||||
values = {child.tag: child.text for child in element}
|
||||
values['id'] = element.attrib['id']
|
||||
return values
|
||||
|
||||
def parse(data):
|
||||
root = xml.etree.ElementTree.fromstring(data)
|
||||
assert root.tag == 'institutions'
|
||||
return [parse_child(element) for element in root]
|
|
@ -1,14 +1,10 @@
|
|||
import logging
|
||||
|
||||
import flask
|
||||
import werkzeug.utils
|
||||
|
||||
import vanth.celery
|
||||
import vanth.pages.tools
|
||||
import vanth.platform.ofxaccount
|
||||
import vanth.platform.ofxsource
|
||||
|
||||
LOGGER = logging.getLogger()
|
||||
blueprint = flask.Blueprint('accounts', __name__)
|
||||
|
||||
@blueprint.route('/accounts/', methods=['GET'])
|
||||
|
@ -44,19 +40,3 @@ def post_account(arguments):
|
|||
def post_update(arguments):
|
||||
vanth.celery.update_account.delay(**arguments)
|
||||
return flask.redirect('/accounts/')
|
||||
|
||||
@blueprint.route('/transactions/', methods=['POST'])
|
||||
@vanth.pages.tools.parse({
|
||||
'account_uuid' : str,
|
||||
})
|
||||
def post_transactions(arguments):
|
||||
if 'transactions' not in flask.request.files:
|
||||
return flask.render_template('error.html', error="You did not include the content of the file in your upload")
|
||||
transactions = flask.request.files['transactions']
|
||||
if transactions.filename == '':
|
||||
return flask.redirect('/accounts/{}/'.format(arguments['account_uuid']))
|
||||
filename = werkzeug.utils.secure_filename(transactions.filename)
|
||||
LOGGER.info("Saving uploaded file %s to %s", transactions.filename, filename)
|
||||
transactions.save(filename)
|
||||
vanth.celery.process_transaction_upload.delay(arguments['account_uuid'], filename)
|
||||
return flask.redirect('/accounts/{}/'.format(arguments['account_uuid']))
|
||||
|
|
|
@ -15,7 +15,7 @@ def parse(args):
|
|||
if supplied is None:
|
||||
missing_parameters.append(key)
|
||||
else:
|
||||
values[key] = converter(supplied) if converter else supplied
|
||||
values[key] = converter(supplied)
|
||||
if missing_parameters:
|
||||
return (json.dumps({'errors': [{
|
||||
'title' : "Missing required paramter '{}'".format(parameter),
|
||||
|
|
|
@ -30,8 +30,6 @@ def _select():
|
|||
vanth.tables.OFXSource.c.uuid.label('source.uuid'),
|
||||
subselect,
|
||||
]).select_from(
|
||||
vanth.tables.OFXAccount.join(vanth.tables.OFXSource)
|
||||
).select_from(
|
||||
subselect
|
||||
).where(
|
||||
vanth.tables.OFXAccount.c.uuid == subselect.c.ofxaccount
|
||||
|
@ -65,12 +63,11 @@ def by_user(user_id):
|
|||
query = query.where(
|
||||
vanth.tables.OFXAccount.c.owner == user_id
|
||||
)
|
||||
LOGGER.debug(query)
|
||||
return _execute_and_convert(query)
|
||||
|
||||
def create(values):
|
||||
engine = chryso.connection.get()
|
||||
values['type'] = values.pop('account_type')
|
||||
|
||||
values['source'] = sqlalchemy.select([
|
||||
vanth.tables.OFXSource.c.uuid
|
||||
]).where(vanth.tables.OFXSource.c.name == values.pop('institution'))
|
||||
|
|
|
@ -20,11 +20,12 @@ def ensure_exists(account, transactions):
|
|||
LOGGER.debug("Have %d new transactions to save", len(new_records))
|
||||
to_insert = [{
|
||||
'amount' : transaction.amount,
|
||||
'available' : transaction.available,
|
||||
'fid' : transaction.id,
|
||||
'memo' : transaction.memo,
|
||||
'name' : transaction.payee,
|
||||
'name' : transaction.name,
|
||||
'ofxaccount' : account['uuid'],
|
||||
'posted' : transaction.date,
|
||||
'posted' : transaction.posted,
|
||||
'type' : transaction.type,
|
||||
'uuid' : uuid.uuid4(),
|
||||
} for transaction in new_records]
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import logging
|
||||
import uuid
|
||||
|
||||
import chryso.connection
|
||||
import sqlalchemy
|
||||
|
||||
import vanth.tables
|
||||
|
||||
|
@ -13,32 +11,10 @@ def _query_and_convert(query):
|
|||
results = engine.execute(query).fetchall()
|
||||
return [dict(result) for result in results]
|
||||
|
||||
def by_uuid(uuid_):
|
||||
query = vanth.tables.OFXSource.select().where(vanth.tables.OFXSource.c.uuid == uuid_)
|
||||
def by_uuid(uuid):
|
||||
query = vanth.tables.OFXSource.select().where(vanth.tables.OFXSource.c.uuid == uuid)
|
||||
return _query_and_convert(query)
|
||||
|
||||
def get():
|
||||
query = vanth.tables.OFXSource.select()
|
||||
return _query_and_convert(query)
|
||||
|
||||
def ensure_exist(institutions):
|
||||
engine = chryso.connection.get()
|
||||
query = sqlalchemy.select([
|
||||
vanth.tables.OFXSource.c.fid,
|
||||
])
|
||||
results = engine.execute(query).fetchall()
|
||||
LOGGER.debug("Found %d OFX sources", len(results))
|
||||
known_records = {result[vanth.tables.OFXSource.c.fid] for result in results}
|
||||
new_records = [institution for institution in institutions if institution['fid'] not in known_records]
|
||||
LOGGER.debug("Have %d new transactions to save", len(new_records))
|
||||
to_insert = [{
|
||||
'name' : institution['name'],
|
||||
'fid' : institution['fid'],
|
||||
'url' : institution['url'],
|
||||
'uuid' : str(uuid.uuid4()),
|
||||
} for institution in new_records]
|
||||
if to_insert:
|
||||
engine.execute(vanth.tables.OFXSource.insert(), to_insert) # pylint: disable=no-value-for-parameter
|
||||
LOGGER.debug("Done inserting %d records", len(new_records))
|
||||
else:
|
||||
LOGGER.debug("Not performing insert, nothing to do")
|
||||
|
|
65
vanth/sgml.py
Normal file
65
vanth/sgml.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class Node(): # pylint: disable=too-few-public-methods
|
||||
def __init__(self, parent, name, children=None, value=None):
|
||||
self.children = children or []
|
||||
self.name = name
|
||||
self.parent = parent
|
||||
self.value = value
|
||||
if parent:
|
||||
parent.children.append(self)
|
||||
|
||||
def __getitem__(self, key):
|
||||
for child in self.children:
|
||||
if child.name == key:
|
||||
return child
|
||||
|
||||
def __repr__(self):
|
||||
return "SGMLNode {} ({})".format(self.name, self.parent.name if self.parent else None)
|
||||
|
||||
def parse(content):
|
||||
state = 'node-content'
|
||||
buf = ''
|
||||
parent_node = None
|
||||
current_node = None
|
||||
for c in content:
|
||||
if c == '<':
|
||||
if state == 'node-content':
|
||||
if buf == '':
|
||||
parent_node = current_node
|
||||
LOGGER.debug("Node content was empty, setting parent node to %s", parent_node)
|
||||
if current_node:
|
||||
current_node.value = buf
|
||||
LOGGER.debug("Set %s to %s", current_node.name, current_node.value)
|
||||
buf = ''
|
||||
state = 'node-name'
|
||||
elif c == '>':
|
||||
if state == 'node-name':
|
||||
LOGGER.debug("Saw opening tag %s. With parent %s", buf, parent_node)
|
||||
state = 'node-content'
|
||||
current_node = Node(parent_node, buf)
|
||||
buf = ''
|
||||
elif state == 'closing-tag':
|
||||
LOGGER.debug("Saw closing tag %s", buf)
|
||||
state = 'closed-tag'
|
||||
parent_node = current_node
|
||||
while parent_node.parent and parent_node.name != buf:
|
||||
parent_node = parent_node.parent
|
||||
parent_node = parent_node.parent
|
||||
buf = ''
|
||||
LOGGER.debug("Set new parent to %s", parent_node.name if parent_node else None)
|
||||
elif c == '/' and buf == '':
|
||||
state = 'closing-tag'
|
||||
parent_node = current_node.parent if current_node else None
|
||||
else:
|
||||
buf += c
|
||||
root = current_node or parent_node
|
||||
while root.parent:
|
||||
root = root.parent
|
||||
return root
|
||||
|
||||
def pformat(node, indent=0):
|
||||
children = '\n'.join(pformat(child, indent+1) for child in node.children)
|
||||
return "{}{}: {}{}".format('\t' * indent, node.name, node.value, "\n" + children if node.children else '')
|
|
@ -1,6 +1,6 @@
|
|||
import chryso.constants
|
||||
from sqlalchemy import (Column, Date, DateTime, ForeignKey, Integer,
|
||||
Numeric, MetaData, String, Table, UniqueConstraint, func, text)
|
||||
from sqlalchemy import (Column, Date, DateTime, ForeignKey, Integer, MetaData,
|
||||
Numeric, String, Table, UniqueConstraint, func, text)
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
metadata = MetaData(naming_convention=chryso.constants.CONVENTION)
|
||||
|
@ -75,46 +75,3 @@ OFXRecord = table('ofxrecord',
|
|||
Column('posted', Date(), nullable=True), # The date the record posted
|
||||
Column('type', String(255), nullable=True), # The type of the record, like 'POS'
|
||||
)
|
||||
|
||||
Pool = table('pool',
|
||||
Column('balance', Integer(), nullable=False), # The balance of the pool, like -10 or +12
|
||||
Column('name', String(255), nullable=False), # The name of the pool, like 'Bills' or 'Heating oil'
|
||||
Column('parent', None, ForeignKey('pool.uuid', name='fk_parent'), nullable=True),
|
||||
Column('user_uri', String(2048), nullable=False), # The URI of the user that created the record
|
||||
UniqueConstraint('name', 'user_uri', name='pool_name_by_user')
|
||||
)
|
||||
|
||||
Spring = table('spring',
|
||||
Column('description', String(1024), nullable=False), # A user-significant description
|
||||
Column('name', String(256), nullable=False), # A user-significant name
|
||||
Column('user_uri', String(2048), nullable=False), # The URI of the user that created the record
|
||||
UniqueConstraint('name', 'user_uri', name='sprint_name_by_user')
|
||||
)
|
||||
|
||||
Sink = table('drain',
|
||||
Column('description', String(1024), nullable=False), # A user-significant description
|
||||
Column('name', String(256), nullable=False), # A user-significant name
|
||||
Column('user_uri', String(2048), nullable=False), # The URI of the user that created the record
|
||||
UniqueConstraint('name', 'user_uri', name='sink_name_by_user')
|
||||
)
|
||||
|
||||
Flow = table('flow',
|
||||
Column('amount', Numeric(precision=20, scale=2, asdecimal=True), nullable=False), # The amount like -$177.91
|
||||
Column('description', String(1024), nullable=False), # The description that is meaningful to the user
|
||||
Column('destination', None, ForeignKey(Pool.c.uuid, name='fk_destination'), nullable=True), # The pool that receives the amount
|
||||
Column('source', None, ForeignKey(Pool.c.uuid, name='fk_source'), nullable=True), # The pool that originally provides the amount
|
||||
)
|
||||
|
||||
Inflow = table('inflow',
|
||||
Column('amount', Numeric(precision=20, scale=2, asdecimal=True), nullable=False), # The amount like -$177.91
|
||||
Column('description', String(1024), nullable=False), # The description that is meaningful to the user
|
||||
Column('destination', None, ForeignKey(Pool.c.uuid, name='fk_destination'), nullable=False), # The pool that receives the amount
|
||||
Column('source', None, ForeignKey(Spring.c.uuid, name='fk_source'), nullable=False), # The sprint that originally provided the amount
|
||||
)
|
||||
|
||||
Outflow = table('outflow',
|
||||
Column('amount', Numeric(precision=20, scale=2, asdecimal=True), nullable=False), # The amount like -$177.91
|
||||
Column('description', String(1024), nullable=False), # The description that is meaningful to the user
|
||||
Column('destination', None, ForeignKey(Sink.c.uuid, name='fk_destination'), nullable=False), # The sink that receives the amount
|
||||
Column('source', None, ForeignKey(Pool.c.uuid, name='fk_source'), nullable=False), # The pool that originally provided the amount
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue