A simple class to represent a Quicken (QIF) file, and a parser to load a QIF file into a sequence of those classes.
It's enough to be useful for writing conversions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #!/usr/bin/python
"""
A simple class to represent a Quicken (QIF) file, and a parser to
load a QIF file into a sequence of those classes.
It's enough to be useful for writing conversions.
"""
import sys
class QifItem:
def __init__(self):
self.order = ['date', 'amount', 'cleared', 'num', 'payee', 'memo', 'address', 'category', 'categoryInSplit', 'memoInSplit', 'amountOfSplit']
self.date = None
self.amount = None
self.cleared = None
self.num = None
self.payee = None
self.memo = None
self.address = None
self.category = None
self.categoryInSplit = None
self.memoInSplit = None
self.amountOfSplit = None
def show(self):
pass
def __repr__(self):
titles = ','.join(self.order)
tmpstring = ','.join( [str(self.__dict__[field]) for field in self.order] )
tmpstring = tmpstring.replace('None', '')
return titles + "," + tmpstring
def dataString(self):
"""
Returns the data of this QIF without a header row
"""
tmpstring = ','.join( [str(self.__dict__[field]) for field in self.order] )
tmpstring = tmpstring.replace('None', '')
return tmpstring
def parseQif(infile):
"""
Parse a qif file and return a list of entries.
infile should be open file-like object (supporting readline() ).
"""
inItem = False
items = []
curItem = QifItem()
line = infile.readline()
while line != '':
if line[0] == '\n': # blank line
pass
elif line[0] == '^': # end of item
# save the item
items.append(curItem)
curItem = QifItem()
elif line[0] == 'D':
curItem.date = line[1:-1]
elif line[0] == 'T':
curItem.amount = line[1:-1]
elif line[0] == 'C':
curItem.cleared = line[1:-1]
elif line[0] == 'P':
curItem.payee = line[1:-1]
elif line[0] == 'M':
curItem.memo = line[1:-1]
elif line[0] == 'A':
curItem.address = line[1:-1]
elif line[0] == 'L':
curItem.category = line[1:-1]
elif line[0] == 'S':
try:
curItem.categoryInSplit.append(";" + line[1:-1])
except AttributeError:
curItem.categoryInSplit = line[1:-1]
elif line[0] == 'E':
try:
curItem.memoInSplit.append(";" + line[1:-1])
except AttributeError:
curItem.memoInSplit = line[1:-1]
elif line[0] == '$':
try:
curItem.amountInSplit.append(";" + line[1:-1])
except AttributeError:
curItem.amountInSplit = line[1:-1]
else:
# don't recognise this line; ignore it
print >> sys.stderr, "Skipping unknown line:\n", line
line = infile.readline()
return items
if __name__ == "__main__":
# read from stdin and write CSV to stdout
items = parseQif(sys.stdin)
print repr(items[0])
for item in items[1:]:
print item.dataString()
|
Financial institutions don't provide customers' data in particularly useful formats, so this module contains a representation of the QIF file item, a parser to load a QIF file, and an example qif2csv program.
Tags: files
format spec. This is based upon the format spec:
http://www.respmech.com/mym2qifw/qif_new.htm
Thanks and a possible change. First, thanks Brian for this. Google found it for me just like that.
Also, in the __repr__ method don't titles and tmpstring need to be joined by a newline, not a comma? Something like:
Cheers, Bill
Small Issue with splits. I found I had to change 'AttributeError' to 'TypeError' to get the split parsing to work properly eg
</pre>