1 |
# DT Wesabe |
2 |
# |
3 |
# Douglas Thrift |
4 |
# |
5 |
# $Id$ |
6 |
|
7 |
from datetime import datetime |
8 |
import decimal |
9 |
from M2Crypto import m2urllib2, SSL |
10 |
import urllib2 |
11 |
from xml.etree import ElementTree |
12 |
|
13 |
EPOCH = datetime.utcfromtimestamp(0) |
14 |
ISO_8601 = '%Y-%m-%dT%H:%M:%SZ' |
15 |
|
16 |
class Account(object): |
17 |
def __init__(self, account): |
18 |
self.id = int(account.find('id').text) |
19 |
self.guid = account.find('guid').text |
20 |
account_number = account.find('account-number') |
21 |
self.account_number = int(account_number.text) if account_number is not None else None |
22 |
self.name = account.find('name').text |
23 |
financial_institution = account.find('financial-institution') |
24 |
self.financial_institution = FinancialInstitution(financial_institution) if financial_institution is not None else None |
25 |
self.account_type = account.find('account-type').text |
26 |
self.currency = Currency(account.find('currency')) |
27 |
current_balance = account.find('current-balance') |
28 |
|
29 |
assert current_balance.get('type') == 'float' |
30 |
|
31 |
self.current_balance = decimal.Decimal(current_balance.text) |
32 |
last_uploaded_at = account.find('last-uploaded-at') |
33 |
|
34 |
if last_uploaded_at is not None: |
35 |
assert last_uploaded_at.get('type') == 'datetime' |
36 |
|
37 |
self.last_uploaded_at = datetime.strptime(last_uploaded_at.text, ISO_8601) |
38 |
else: |
39 |
self.last_uploaded_at = EPOCH |
40 |
|
41 |
self.txaction_count = int(account.find('txaction-count').text) |
42 |
oldest_txaction = account.find('oldest-txaction') |
43 |
|
44 |
if oldest_txaction is not None: |
45 |
assert oldest_txaction.get('type') == 'datetime' |
46 |
|
47 |
self.oldest_txaction = datetime.strptime(oldest_txaction.text, ISO_8601) |
48 |
else: |
49 |
self.oldest_txaction = EPOCH |
50 |
|
51 |
newest_txaction = account.find('newest-txaction') |
52 |
|
53 |
if newest_txaction is not None: |
54 |
assert newest_txaction.get('type') == 'datetime' |
55 |
|
56 |
self.newest_txaction = datetime.strptime(newest_txaction.text, ISO_8601) |
57 |
else: |
58 |
self.newest_txaction = EPOCH |
59 |
|
60 |
class Currency(object): |
61 |
def __init__(self, currency): |
62 |
self.symbol = currency.get('symbol') |
63 |
self.separator = currency.get('separator') |
64 |
self.delimiter = currency.get('delimiter') |
65 |
self.decimal_places = int(currency.get('decimal_places')) |
66 |
self.name = currency.text |
67 |
|
68 |
class FinancialInstitution(object): |
69 |
def __init__(self, financial_institution): |
70 |
self.id = financial_institution.find('id').text |
71 |
self.name = financial_institution.find('name').text |
72 |
|
73 |
class Wesabe(object): |
74 |
def __init__(self, email, password): |
75 |
context = SSL.Context() |
76 |
|
77 |
context.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth = 9) |
78 |
context.load_verify_locations('/etc/ssl/cert.pem') |
79 |
|
80 |
handler = urllib2.HTTPBasicAuthHandler() |
81 |
|
82 |
handler.add_password('RESTRICTED', 'https://www.wesabe.com/', email, password) |
83 |
handler.add_password('Wesabe Upload API', 'https://api.wesabe.com/rest/upload/statement', email, password) |
84 |
|
85 |
self.opener = m2urllib2.build_opener(context, handler) |
86 |
|
87 |
def accounts(self): |
88 |
accounts = ElementTree.parse(self.opener.open('https://www.wesabe.com/accounts.xml')).getroot() |
89 |
|
90 |
return map(lambda account: Account(account), accounts.getchildren()) |
91 |
|
92 |
def upload(self, account_number, account_type, wesabe_id, data, balance = None): |
93 |
upload = ElementTree.Element('upload') |
94 |
statement = ElementTree.SubElement(upload, 'statement') |
95 |
|
96 |
statement.set('acctid', str(account_number)) |
97 |
statement.set('accttype', account_type) |
98 |
statement.set('wesabe_id', wesabe_id) |
99 |
|
100 |
if balance is not None: |
101 |
statement.set('balance', str(balance)) |
102 |
|
103 |
statement.text = data |
104 |
|
105 |
self.opener.open('https://api.wesabe.com/rest/upload/statement', ElementTree.tostring(upload, 'UTF-8')) |