Skip to content

Commit

Permalink
Code and README
Browse files Browse the repository at this point in the history
  • Loading branch information
bartbroere committed Apr 10, 2017
1 parent 9c14f39 commit cb74180
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 2 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 Bart Broere

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,58 @@
# toledger
Convert ING transactions from CSV format to Ledger
# ToLedger
ToLedger originated as a personal project to convert my bank statements
to the open source [ledger](http://ledger-cli.org/) format. I have made
some changes to make it more generally applicable, allowing for other
banks to be added.

Currently this library is only known to work with the export files of
ING Bank (NL), but extending it for similar CSV exports should be trivial.

```
Usage:
toledger.py <format>
toledger.py <format> <input>
toledger.py <format> [(-a | --append)] <input> <output>
toledger.py <format> [(-f | --from)] <input> <output>
toledger.py (-h | --help)
Options:
-a --append Append to the output file if it exists.
-f --from Account to get balances from.
Default Equity:Unspecified:[IBAN]
-t --to Account to send transactions to.
Default Expenses:Unspecified:[IBAN]
-c --code Use the currency code, e.g. EUR
-s --symbol Use the currency symbol, e.g. €
-h --help Show the usage guidelines.
```

## Installation
Apart from downloading this repository, you can install the latest
release with ``pip install toledger``

## Contributing
If you wish to contribute to this project, here's a feature wishlist:

+ Add different formats for different banks
(see toledger/bankstatements.py)
+ Interactive mode, manually adding and changing transaction details
+ Smart transaction labeling, based on specification files or existing
Ledger files
+ Checking the output file for duplicates
+ Convert to using a Ledger library for Python, instead of writing
directly to files (better and more output formatting options)
+ Save the meta information in the output file
+ Python 2 compatibility
+ Unit tests
+ Any other suggestions are also welcome, especially if they improve
extensibility and compatibility

There are also some design conventions:

+ [PEP 8](https://www.python.org/dev/peps/pep-0008/) (within reason)
+ All options should be reachable from the command line, using
[docopt](http://docopt.org)
+ [stdin and stdout compatibility](https://docs.python.org/3/library/sys.html#sys.stdin)
+ Error handling with
[built-in exceptions](https://docs.python.org/3.6/library/exceptions.html)
(with custom messages)
Empty file added setup.cfg
Empty file.
Empty file added setup.py
Empty file.
21 changes: 21 additions & 0 deletions toledger/bankstatements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""bankstatements.py
Contains metadata for parsing input files
"""

ing_nl = {"fields": {"Datum": "date",
"Naam / Omschrijving": "name",
"Rekening": "source",
"Tegenrekening": "destination",
"Code": "meta",
"Af Bij": "direction",
"Bedrag (EUR)": "amount",
"MutatieSoort": "meta",
"Mededelingen": "meta"},
"interpretation": {"directionout": "Af",
"currency": {"symbol": "", #TODO fix unicode issue
"code": "EUR"},
"separators": {"thousandseparator": ".",
"decimalseparator": ","},
"dateformat": "%Y%m%d"},
"properties": {"quotechar": "\"",
"delimiter": ","}}
99 changes: 99 additions & 0 deletions toledger/toledger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""ToLedger: Bank statements to ledger format
Usage:
toledger.py <format> [options]
toledger.py <format> [options] <input>
toledger.py <format> [options] <input> <output>
toledger.py (-h | --help)
Options:
-a --append Append to the output file if it exists.
-b --balance Ensure that all output is balanced.
Choose this option if the output must be a
standalone ledger file. These files should always
have a balance of 0 to be valid.
-f --from Account to get balances from.
Default Equity:[Unspecified | IBAN]
-t --to Account to send transactions to.
Default Expenses:[Unspecified | IBAN]
-n --name Account name. Default Assets:[IBAN]
-c --code Use the currency code, e.g. EUR
-s --symbol Use the currency symbol, e.g. €
-h --help Show the usage guidelines.
"""
from docopt import docopt
from sys import stdin, stdout
import csv
import bankstatements
from time import strftime, strptime

def main(a):
with open(a["<input>"], "r") as inputfile:
input = csv.reader(inputfile, **properties(a["<format>"]))
filemode = "a" if a["--append"] else "w"
if not a["<output>"]: outputlocation = stdout
else: outputlocation = open(a["<output>"], filemode)
with outputlocation as output:
i = 0
for inputentry in input:
if i == 0:
header = inputentry
else:
parsedentry = parse(header, inputentry, a["<format>"])
# TODO: Interactive mode
write(output, **parsedentry)
i += 1
return 0

def parse(header, entry, format):
# TODO: Smart transaction labeling
parsed = {"meta": {}}
for name, field in specification(format)["fields"].items():
if field == "meta": parsed["meta"][name] = entry[header.index(name)]
else: parsed[field] = entry[header.index(name)]
for name, field in specification(format)["interpretation"].items():
if name == "directionout":
parsed["direction"] = "-" if parsed["direction"] == field else ""
elif name == "currency":
if "--code" in a:
parsed["amount"] = " ".join([parsed["amount"], field["code"]])
elif "--symbol" in a:
parsed["amount"] = " ".join([field["symbol"], parsed["amount"]])
elif name == "separators":
parsed["amount"] = parsed["amount"].replace(
field["thousandseparator"], "")
parsed["amount"] = parsed["amount"].replace(
field["decimalseparator"], ".")
elif name == "dateformat":
parsed["date"] = strftime("%Y-%m-%d", strptime(parsed["date"], field))
return parsed

def specification(format):
try:
specification = getattr(bankstatements, format)
return specification
except:
raise NotImplementedError("The specified format does not exist")

def properties(format): return specification(format)["properties"]

def write(output, **data):
output.write("\n")
output.write(data["date"]+" "+data["name"])
output.write("\n\t")
if data["direction"] == "-":
output.write("Expenses:"+max([data["destination"], "Unspecified"], key=len)+
" "+"\n\t")
else:
output.write("Equity:"+max([data["destination"], "Unspecified"], key=len)+
" "+"\n\t")
output.write("Assets:"+data["source"]+"\t"+data["direction"]+
data["amount"]+"\n")
# TODO Save metadata to outputfile
output.write("\n")
return

if __name__ == '__main__':
a = docopt(__doc__, version='ToLedger 1.0')
main(a)

0 comments on commit cb74180

Please sign in to comment.