Skip to content

Commit

Permalink
Version 1.0 (MVP)
Browse files Browse the repository at this point in the history
Submitted to PyPi
  • Loading branch information
bartbroere committed Apr 10, 2017
1 parent fc29c9a commit 76e152c
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 0 deletions.
21 changes: 21 additions & 0 deletions build/lib/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 build/lib/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.
--from=<from> Account to get balances from.
Default Equity:[Unspecified | IBAN]
--to=<to> Account to send transactions to.
Default Expenses:[Unspecified | IBAN]
--name=<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)
99 changes: 99 additions & 0 deletions build/scripts-3.5/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.
--from=<from> Account to get balances from.
Default Equity:[Unspecified | IBAN]
--to=<to> Account to send transactions to.
Default Expenses:[Unspecified | IBAN]
--name=<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)
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[metadata]
description-file = README.md
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def readme():
long_description=readme(),
keywords=["ledger", "convert", "conversion", "bank", "ing", "nl", "accounting", "command", "commandline", "cli"],
packages=['toledger'],
scripts=['toledger/toledger.py'],
url='https://github.com/bartbroere/toledger/',
license='MIT',
author='Bart Broere',
Expand Down

0 comments on commit 76e152c

Please sign in to comment.