Skip to content

Commit 4b77137

Browse files
authored
Merge pull request #11 from honzajavorek/honzajavorek/revive
Revive
2 parents dcd7488 + 35f8a55 commit 4b77137

11 files changed

+718
-81
lines changed

.travis.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
sudo: false
2+
language: "python"
3+
cache: "pip"
4+
python:
5+
- "2.7"
6+
- "3.5"
7+
addons:
8+
apt:
9+
packages:
10+
- pandoc
11+
env:
12+
global:
13+
- PYPI_USERNAME=honzajavorek
14+
- secure: "jOUhNuGVbKI5+5mw5UhMayjAhqv5L1w8gmjmHgZOlY1rNA00P3TW5asy1wwbLxTfDAudA+WlEBiakyp3qwZhe41A9rzMGTjxZkp3257gJZH0/Tq4DLI1OCRffU/3RgyUJT2SJc0cneec6U5//iXeHvneGu6qyiC46nIuxOHau0U="
15+
install:
16+
- "pip install -e .[tests,release]"
17+
script:
18+
- "flake8"
19+
- "py.test"
20+
after_success:
21+
- "git config --global user.name 'Honza Javorek'"
22+
- "git config --global user.email '[email protected]'"
23+
- "python setup.py publish"

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2013, Jan Javorek <[email protected]>
1+
Copyright (c) 2013-?, Honza Javorek <[email protected]>
22

33
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
44

MANIFEST.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
include README.md LICENSE requirements.txt
1+
include README.md setup.cfg LICENSE

README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
# fiobank
22

3-
Little library implementing [Fio Bank API](http://www.fio.cz/bank-services/internetbanking-api) in Python.
3+
[![PyPI version](https://badge.fury.io/py/redis-collections.svg)](https://badge.fury.io/py/redis-collections)
4+
[![Build Status](https://travis-ci.org/honzajavorek/fiobank.svg?branch=master)](https://travis-ci.org/honzajavorek/fiobank)
5+
6+
[Fio Bank API](http://www.fio.cz/bank-services/internetbanking-api) in Python.
47

58
## Installation
69

7-
```bash
10+
```sh
811
$ pip install fiobank
912
```
1013

1114
## Usage
1215

13-
Initialization of client:
16+
First, [get your API token](https://www.fio.cz/ib2/wicket/page/NastaveniPage?3). Initialization of the client:
1417

1518
```python
1619
>>> from fiobank import FioBank
@@ -24,15 +27,15 @@ Account information:
2427
{'currency': 'CZK', 'account_number_full': 'XXXXXXXXXX/2010', 'balance': 42.00, 'account_number': 'XXXXXXXXXX', 'bank_code': '2010'}
2528
```
2629

27-
Listing transactions within time period:
30+
Listing transactions within a time period:
2831

2932
```python
3033
>>> gen = client.period('2013-01-20', '2013-03-20')
3134
>>> list(gen)[0]
3235
{'comment': u'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 'recipient_message': u'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 'user_identifiaction': u'N\xe1kup: IKEA CR, BRNO, CZ, dne 17.1.2013, \u010d\xe1stka 2769.00 CZK', 'currency': 'CZK', 'amount': -2769.0, 'instruction_id': 'XXXXXXXXXX', 'executor': u'Vilém Fusek', 'date': datetime.date(2013, 1, 20), 'type': u'Platba kartou', 'transaction_id': 'XXXXXXXXXX'}
3336
```
3437

35-
Listing transactions from single account statement:
38+
Listing transactions from a single account statement:
3639

3740
```python
3841
>>> client.statement(2013, 1) # 1 is January only by coincidence - arguments mean 'first statement of 2013'
@@ -51,6 +54,6 @@ For further information [read code](https://github.com/honzajavorek/fiobank/blob
5154

5255
## License: ISC
5356

54-
© 2013 Jan Javorek <[email protected]>
57+
© 2013-? Honza Javorek <[email protected]>
5558

5659
This work is licensed under [ISC license](https://en.wikipedia.org/wiki/ISC_license).

fiobank.py

+69-46
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
# -*- coding: utf-8 -*-
22

3+
4+
from __future__ import unicode_literals
5+
36
import re
47
from datetime import datetime, date
58

9+
import six
610
import requests
711

812

913
__all__ = ('FioBank',)
1014

1115

16+
str = six.text_type
17+
18+
1219
def coerce_date(value):
13-
if isinstance(value, date):
14-
return value
15-
elif isinstance(value, datetime):
20+
if isinstance(value, datetime):
1621
return value.date()
22+
elif isinstance(value, date):
23+
return value
1724
else:
1825
return datetime.strptime(value[:10], '%Y-%m-%d').date()
1926

2027

2128
def sanitize_value(value, convert=None):
22-
if isinstance(value, basestring):
29+
if isinstance(value, six.string_types):
2330
value = value.strip() or None
24-
if convert and value:
31+
if convert and value is not None:
2532
return convert(value)
2633
return value
2734

@@ -38,38 +45,39 @@ class FioBank(object):
3845
'set-last-date': 'set-last-date/{token}/{from_date}/',
3946
}
4047

48+
# http://www.fio.cz/xsd/IBSchema.xsd
4149
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),
6169
}
6270

6371
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),
7078
}
7179

72-
_amount_re = re.compile(r'\-?[\d+](\.\d+)? [A-Z]{3}')
80+
_amount_re = re.compile(r'\-?\d+(\.\d+)? [A-Z]{3}')
7381

7482
def __init__(self, token):
7583
self.token = token
@@ -89,22 +97,22 @@ def _parse_info(self, data):
8997
# parse data from API
9098
info = {}
9199
for key, value in data['accountStatement']['info'].items():
100+
key = key.lower()
92101
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)
95104
info[field_name] = value
96105

97106
# make some refinements
98-
info['account_number_full'] = (info['account_number'] +
99-
'/' + info['bank_code'])
107+
self._add_account_number_full(info)
100108

101109
# return data
102110
return info
103111

104112
def _parse_transactions(self, data):
105113
schema = self.transaction_schema
106114
try:
107-
entries = data['accountStatement']['transactionList']['transaction']
115+
entries = data['accountStatement']['transactionList']['transaction'] # NOQA
108116
except TypeError:
109117
entries = []
110118

@@ -114,27 +122,41 @@ def _parse_transactions(self, data):
114122
for column_name, column_data in entry.items():
115123
if not column_data:
116124
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)
119127
trans[field_name] = value
120128

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+
121133
# make some refinements
134+
specification = trans.get('specification')
122135
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):
124137
amount, currency = trans['specification'].split(' ')
125138
trans['original_amount'] = float(amount)
126139
trans['original_currency'] = currency
140+
else:
141+
trans['original_amount'] = None
142+
trans['original_currency'] = None
127143

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)
134145

135146
# generate transaction data
136147
yield trans
137148

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+
138160
def info(self):
139161
today = date.today()
140162
data = self._request('periods', from_date=today, to_date=today)
@@ -151,7 +173,8 @@ def statement(self, year, number):
151173
return self._parse_transactions(data)
152174

153175
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.')
155178

156179
if from_id:
157180
self._request('set-last-id', from_id=from_id)

setup.cfg

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[wheel]
2+
universal = 1
3+
4+
[flake8]
5+
exclude = env
6+
7+
[pytest]
8+
testpaths = tests
9+
norecursedirs = env venv .git
10+
11+
[aliases]
12+
test = pytest
13+
14+
[semantic_release]
15+
version_variable = setup.py:version

setup.py

+31-27
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,63 @@
11
# -*- coding: utf-8 -*-
22

33

4-
import os
4+
from __future__ import print_function
5+
56
import sys
6-
import shlex
7-
import subprocess
7+
from setuptools import setup
88

99
try:
10-
from setuptools import setup, find_packages
10+
from semantic_release import setup_hook
11+
setup_hook(sys.argv)
1112
except ImportError:
12-
from distutils.core import setup, find_packages # NOQA
13+
message = "Unable to locate 'semantic_release', releasing won't work"
14+
print(message, file=sys.stderr)
1315

14-
# Hack to prevent stupid "TypeError: 'NoneType' object is not callable"
15-
# error in multiprocessing/util.py _exit_function when running `python
16-
# setup.py test`
1716
try:
18-
import multiprocessing # NOQA
17+
import pypandoc
18+
long_description = pypandoc.convert_file('README.md', 'rst')
1919
except ImportError:
20-
pass
21-
22-
23-
base_path = os.path.dirname(__file__)
20+
message = (
21+
"Unable to locate 'pypandoc', long description of the 'fiobank'"
22+
"package won't be available"
23+
)
24+
print(message, file=sys.stderr)
25+
long_description = ''
2426

2527

26-
version = '0.0.5'
28+
version = '1.0.0'
2729

2830

29-
# release a version, publish to GitHub and PyPI
30-
if sys.argv[-1] == 'publish':
31-
command = lambda cmd: subprocess.check_call(shlex.split(cmd))
32-
command('git tag v' + version)
33-
command('git push --tags origin master:master')
34-
command('python setup.py sdist upload')
35-
sys.exit()
31+
install_requires = ['requests', 'six']
32+
tests_require = ['pytest-runner', 'pytest', 'flake8', 'responses', 'mock']
33+
release_requires = ['pypandoc', 'python-semantic-release']
3634

3735

3836
setup(
3937
name='fiobank',
4038
version=version,
41-
description='Little library implementing Fio Bank API in Python',
42-
long_description=open('README.md').read(),
39+
description='Fio Bank API in Python',
40+
long_description=long_description,
4341
author='Honza Javorek',
44-
author_email='[email protected]',
42+
author_email='[email protected]',
4543
url='https://github.com/honzajavorek/fiobank',
4644
license=open('LICENSE').read(),
4745
py_modules=('fiobank',),
48-
install_requires=['requests>=1.0.0'],
46+
install_requires=install_requires,
47+
tests_require=tests_require,
48+
extras_require={
49+
'tests': tests_require,
50+
'release': release_requires,
51+
},
4952
classifiers=(
50-
'Development Status :: 4 - Beta',
53+
'Development Status :: 5 - Production/Stable',
5154
'Intended Audience :: Developers',
5255
'License :: OSI Approved :: ISC License (ISCL)',
5356
'Programming Language :: Python',
5457
'Programming Language :: Python :: 2.6',
5558
'Programming Language :: Python :: 2.7',
5659
'Topic :: Software Development :: Libraries :: Python Modules',
5760
'Topic :: Internet',
58-
)
61+
),
62+
keywords='bank api wrapper sdk fio'
5963
)

0 commit comments

Comments
 (0)