1
1
# -*- coding: utf-8 -*-
2
2
3
+
4
+ from __future__ import unicode_literals
5
+
3
6
import re
4
7
from datetime import datetime , date
5
8
9
+ import six
6
10
import requests
7
11
8
12
9
13
__all__ = ('FioBank' ,)
10
14
11
15
16
+ str = six .text_type
17
+
18
+
12
19
def coerce_date (value ):
13
- if isinstance (value , date ):
14
- return value
15
- elif isinstance (value , datetime ):
20
+ if isinstance (value , datetime ):
16
21
return value .date ()
22
+ elif isinstance (value , date ):
23
+ return value
17
24
else :
18
25
return datetime .strptime (value [:10 ], '%Y-%m-%d' ).date ()
19
26
20
27
21
28
def sanitize_value (value , convert = None ):
22
- if isinstance (value , basestring ):
29
+ if isinstance (value , six . string_types ):
23
30
value = value .strip () or None
24
- if convert and value :
31
+ if convert and value is not None :
25
32
return convert (value )
26
33
return value
27
34
@@ -38,38 +45,39 @@ class FioBank(object):
38
45
'set-last-date' : 'set-last-date/{token}/{from_date}/' ,
39
46
}
40
47
48
+ # http://www.fio.cz/xsd/IBSchema.xsd
41
49
transaction_schema = {
42
- u'ID pohybu ' : ('transaction_id' , str ),
43
- u'Datum ' : ('date' , unicode ),
44
- u'Objem ' : ('amount' , float ),
45
- u'Měna ' : ('currency' , str ),
46
- u'Protiúčet ' : ('account_number' , str ),
47
- u'Název protiúčtu ' : ('account_name' , unicode ),
48
- u'Kód banky ' : ('bank_code' , str ),
49
- u'BIC ' : ('bic' , str ),
50
- u'Název banky ' : ('bank_name' , unicode ),
51
- u'KS ' : ('constant_symbol' , str ),
52
- u'VS ' : ('variable_symbol' , str ),
53
- u'SS ' : ('specific_symbol' , str ),
54
- u'Uživatelská identifikace ' : ('user_identification' , unicode ),
55
- u'Zpráva pro příjemce ' : ('recipient_message' , unicode ),
56
- u'Typ ' : ('type' , unicode ),
57
- u'Provedl ' : ('executor' , unicode ),
58
- u'Upřesnění ' : ('specification' , unicode ),
59
- u'Komentář ' : ('comment' , unicode ),
60
- u'ID pokynu ' : ('instruction_id' , str ),
50
+ 'column22 ' : ('transaction_id' , str ),
51
+ 'column0 ' : ('date' , coerce_date ),
52
+ 'column1 ' : ('amount' , float ),
53
+ 'column14 ' : ('currency' , str ),
54
+ 'column2 ' : ('account_number' , str ),
55
+ 'column10 ' : ('account_name' , str ),
56
+ 'column3 ' : ('bank_code' , str ),
57
+ 'column26 ' : ('bic' , str ),
58
+ 'column12 ' : ('bank_name' , str ),
59
+ 'column4 ' : ('constant_symbol' , str ),
60
+ 'column5 ' : ('variable_symbol' , str ),
61
+ 'column6 ' : ('specific_symbol' , str ),
62
+ 'column7 ' : ('user_identification' , str ),
63
+ 'column16 ' : ('recipient_message' , str ),
64
+ 'column8 ' : ('type' , str ),
65
+ 'column9 ' : ('executor' , str ),
66
+ 'column18 ' : ('specification' , str ),
67
+ 'column25 ' : ('comment' , str ),
68
+ 'column17 ' : ('instruction_id' , str ),
61
69
}
62
70
63
71
info_schema = {
64
- u'accountId ' : ('account_number' , str ),
65
- u'bankId ' : ('bank_code' , str ),
66
- u 'currency' : ('currency' , str ),
67
- u'IBAN ' : ('iban' , str ),
68
- u'BIC ' : ('bic' , str ),
69
- u'closingBalance ' : ('balance' , float ),
72
+ 'accountid ' : ('account_number' , str ),
73
+ 'bankid ' : ('bank_code' , str ),
74
+ 'currency' : ('currency' , str ),
75
+ 'iban ' : ('iban' , str ),
76
+ 'bic ' : ('bic' , str ),
77
+ 'closingbalance ' : ('balance' , float ),
70
78
}
71
79
72
- _amount_re = re .compile (r'\-?[ \d+] (\.\d+)? [A-Z]{3}' )
80
+ _amount_re = re .compile (r'\-?\d+(\.\d+)? [A-Z]{3}' )
73
81
74
82
def __init__ (self , token ):
75
83
self .token = token
@@ -89,22 +97,22 @@ def _parse_info(self, data):
89
97
# parse data from API
90
98
info = {}
91
99
for key , value in data ['accountStatement' ]['info' ].items ():
100
+ key = key .lower ()
92
101
if key in self .info_schema :
93
- field_name , type_ = self .info_schema [key ]
94
- value = sanitize_value (value , type_ )
102
+ field_name , convert = self .info_schema [key ]
103
+ value = sanitize_value (value , convert )
95
104
info [field_name ] = value
96
105
97
106
# make some refinements
98
- info ['account_number_full' ] = (info ['account_number' ] +
99
- '/' + info ['bank_code' ])
107
+ self ._add_account_number_full (info )
100
108
101
109
# return data
102
110
return info
103
111
104
112
def _parse_transactions (self , data ):
105
113
schema = self .transaction_schema
106
114
try :
107
- entries = data ['accountStatement' ]['transactionList' ]['transaction' ]
115
+ entries = data ['accountStatement' ]['transactionList' ]['transaction' ] # NOQA
108
116
except TypeError :
109
117
entries = []
110
118
@@ -114,27 +122,41 @@ def _parse_transactions(self, data):
114
122
for column_name , column_data in entry .items ():
115
123
if not column_data :
116
124
continue
117
- field_name , type_ = schema [column_data [ 'name' ] ]
118
- value = sanitize_value (column_data ['value' ], type_ )
125
+ field_name , convert = schema [column_name . lower () ]
126
+ value = sanitize_value (column_data ['value' ], convert )
119
127
trans [field_name ] = value
120
128
129
+ # add missing fileds with None values
130
+ for column_data_name , (field_name , convert ) in schema .items ():
131
+ trans .setdefault (field_name , None )
132
+
121
133
# make some refinements
134
+ specification = trans .get ('specification' )
122
135
is_amount = self ._amount_re .match
123
- if ' specification' in trans and is_amount (trans [ ' specification' ] ):
136
+ if specification is not None and is_amount (specification ):
124
137
amount , currency = trans ['specification' ].split (' ' )
125
138
trans ['original_amount' ] = float (amount )
126
139
trans ['original_currency' ] = currency
140
+ else :
141
+ trans ['original_amount' ] = None
142
+ trans ['original_currency' ] = None
127
143
128
- if 'date' in trans :
129
- trans ['date' ] = coerce_date (trans ['date' ])
130
-
131
- if 'account_number' in trans and 'bank_code' in trans :
132
- trans ['account_number_full' ] = (trans ['account_number' ] +
133
- '/' + trans ['bank_code' ])
144
+ self ._add_account_number_full (trans )
134
145
135
146
# generate transaction data
136
147
yield trans
137
148
149
+ def _add_account_number_full (self , obj ):
150
+ account_number = obj .get ('account_number' )
151
+ bank_code = obj .get ('bank_code' )
152
+
153
+ if account_number is not None and bank_code is not None :
154
+ account_number_full = '{}/{}' .format (account_number , bank_code )
155
+ else :
156
+ account_number_full = None
157
+
158
+ obj ['account_number_full' ] = account_number_full
159
+
138
160
def info (self ):
139
161
today = date .today ()
140
162
data = self ._request ('periods' , from_date = today , to_date = today )
@@ -151,7 +173,8 @@ def statement(self, year, number):
151
173
return self ._parse_transactions (data )
152
174
153
175
def last (self , from_id = None , from_date = None ):
154
- assert not (from_id and from_date ), "Only one constraint is allowed."
176
+ if from_id and from_date :
177
+ raise ValueError ('Only one constraint is allowed.' )
155
178
156
179
if from_id :
157
180
self ._request ('set-last-id' , from_id = from_id )
0 commit comments