Skip to content

Instantly share code, notes, and snippets.

@HeinrichHartmann
Created July 22, 2014 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HeinrichHartmann/59f1b3464a2085ee9514 to your computer and use it in GitHub Desktop.
Save HeinrichHartmann/59f1b3464a2085ee9514 to your computer and use it in GitHub Desktop.
Bank Account Analytics for Sparda-Bank csv files
"""
Computes a summary of (unique) bank transactions from CSV files
USAGE:
python account-summary.py account_statment1.csv account_statement2.csv ...
OUTPUT:
CSV printed to stdout with the following colums
ACCOUNT,
DATE(Wertstellungstag),
DESCRIPTION(Verwendungszweck),
VALUE(Umsatz),
SALDO
(C) Heinrich Hartmann, 2014
"""
from __future__ import print_function
from datetime import datetime
import re
import sys
import functools as ft
ERR = ft.partial(print, file=sys.stderr)
PRINT = ft.partial(print, file=sys.stdout)
#
# Account Management
#
class Transaction(object):
"""
Bank Transaction class
Stores value as int in cents
"""
# Attributes
date = None # : datetime
description = None # : str
value_cnt = None # : int in CNT
def __init__(self, date, description, value_eur):
"""
Create transaction object
Arguments:
date: datetime -- date of transaction
descripiton: str -- textual description
value_eur: float -- value of transaction in EUR
"""
self.date = type_check(datetime, date)
self.description = type_check(str, description)
self.value_cnt = int(100 * type_check(float, value_eur))
def get_date(self, pattern = "%Y-%m-%d"):
return self.date.strftime("%Y-%m-%d")
def get_datetime(self):
return self.date
def get_cnt(self):
return self.value_cnt
def get_eur(self):
return float(self.value_cnt) / 100
def get_description(self):
return self.description
def __str__(self):
return "".join([
"Transaction: ",
self.get_date() + "\t",
self.get_description() + "\t",
str(self.get_eur()) + " EUR"
])
def uid(self):
return self.__str__()
class Account(object):
"""
Stores a list of transactions.
Checks for unicity on add.
"""
T_set = set([])
T_list = []
def __init__(self, name):
self.name = name
def add_transaction(self, transaction):
"""
Add (unique) transaction to account.
"""
assert_type(Transaction, transaction)
if transaction.uid() in self.T_set:
ERR("Transaction allready added", transaction)
return
self.T_set.add(transaction.uid())
self.T_list.append(transaction)
def print_transactions(self, stream=sys.stdout):
"""
Prints all transactions ot stream = sys.stdout
"""
# sort by date
self.T_list.sort(key=lambda t : t.get_datetime())
saldo_cnt = 0
for transaction in self.T_list:
saldo_cnt += transaction.get_cnt()
print("\t".join(
[
transaction.get_date(),
str(transaction.get_eur()),
str(saldo_cnt/100.),
transaction.get_description()
]
), file=stream)
#
# Sparda Format Conversion
#
class SpardaFileParser(object):
"""
Parses a file downloaded from Sparda Web Interface
"""
file_handle = None # : file -- file handle
def __init__(self, file_handle):
self.file_handle = file_handle
def __iter__(self):
self.file_handle.seek(0)
for i, line in enumerate(self.file_handle):
if i < 6: # 5 line headder
continue
if line.startswith("*"): # remark in the last row
break
yield self.parse_sparda_row(line)
@classmethod
def parse_date(cls, r_date):
day, month, year = re.findall("(\d{2}).(\d{2}).(\d{4})", r_date)[0]
return datetime(int(year), int(month), int(day))
@classmethod
def parse_sparda_row(cls, line):
"""
Arguments:
line: string -- transation row in sparda fromat
Returns:
date: datetime -- date of transaction
descr: string -- textual description
value: int -- value in EUR-cnts
"""
fields = line.split("\t")
fields[4] = fields[4].strip("\n")
r_date, _, r_description, r_value, _ = fields
value = float(r_value.replace(",","."))
date = cls.parse_date(r_date)
return Transaction(date, r_description, value)
#
# Typing Helper
#
def assert_type(type_obj, obj):
"Raises Exception if obj is not of type type_obj."
if not type(obj) is type_obj:
raise Exception("Type Mismatch")
# TODO: raise proper exception
def type_check(type_obj, obj):
"Returns obj if type matches type_obj."
assert_type(type_obj, obj)
return obj
#
# Control Flow
#
if __name__ == "__main__":
account = Account("")
for path in sys.argv[1:]:
ERR("Inserting file", path)
for transaction in SpardaFileParser(open(path)):
account.add_transaction(transaction)
account.print_transactions()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment