Saturday, May 12, 2007

Wesabe Export Wrangler

I just uploaded gleaner.py, a Python module to parse Wesabe's XML export and make little text reports based on their content. It really only consists of two things at this point: read_txactions(), a function that reads "accounts.xml" in the current directory and returns a list of Transaction objects, and a set of "filter functions" that operate on the Transaction objects.

The filter functions are the ones that are in all caps. So far I have TAG, MERCHANT, ACCOUNT, AFTER, BEFORE, MONTH, AND, OR and NOT. To use a filter function, you call it with what you want to match and it returns a new function that returns true when passed Transactions that meet that criteria and false for those that don't make the grade. In the example below, TAG('charlie') creates a function that returns true for any transaction that has the 'charlie' tag. MERCHANT and ACCOUNT are fairly similar to TAG. They take a piece of text and return true if a transaction's merchant is the same as that text or its account is the same respectively. AFTER and BEFORE take a day formatted like '2007-05-01' and make a filter that matches transactions after or before that date. MONTH matches any transaction in a given month. It either takes a full month like '2007-05' or just a month number like '5'. If given the second form, it assumes the month is in the current year.

An example report is probably better than actually talking through this:

from gleaner import *

txs = read_txactions()

total = 0
for month in [MONTH(m) for m in '3', '4', '5']:

spending = sum(t.amount for t in filter(AND(TAG('charlie'), month), txs))

print '%s: %s' % (month.name, spending)
total += spending

print 'Total: ', total

I tag everything I spend on myself with 'charlie'. So
AND(TAG('charlie'), month)
just matches what I spent on myself in a given month. When I run it(with fictional data), the output is:
$ python charlie_report.py
during Mar 2007: -457.23
during Apr 2007: -318.45
during May 2007: -412.58
Total: -1188.26

Of course, that's pretty easily figured out from the things built-in to Wesabe's site. Here's a more complicated report:

from gleaner import *

txs = read_txactions()

def sum_txactions(*filters):
return sum(t.amount for t in filter(AND(*filters), txs))

def month_summary(name, income, spendable, spent, diff):

sep = '=' * (len(name) + 2)

print '''%(name)s
%(sep)s
Income: %(income)s
Spendable: %(spendable)s
Spent: %(spent)s
Diff: %(diff)s
'''
% locals()

net = 0

computer_spending = sum_txactions(TAG('computer'), TAG('chelsea'))

for month in [MONTH(m) for m in '3', '4', '5']:

income = sum_txactions(TAG('birdnerd'), month)
computer_deduct = 200

#Don't deduct for March since computer was purchased after that
if 'Mar' in month.name:
computer_deduct = 0

spendable = income/2 - computer_deduct
spent = sum_txactions(TAG('chelsea'), month, NOT(TAG('computer')))

diff = spendable + spent
month_summary(month.name, income, spendable, spent, diff)

net += diff
computer_spending += computer_deduct

print 'Remaining Laptop: %s' % computer_spending

print 'Net: %s' % net

My lovely wife sells her art through the net under the moniker birdnerd. She wants to put half of her birdnerd profits back into the family's coffers and then spend the other half on things that strike her fancy. To throw a wrench into the works, she bought a most excellent MacBook a month ago and would like to pay for that $200 a month out of the "strike her fancy" half of the birdnerd profits. This report prints out the birdnerd income month by month, figures out how much of that she can spend on herself minus the monthly laptop payment, and keeps track of how much of the laptop remains to be paid off to print at the end.

Right now this doesn't handle using tag splits to determine amounts since the tag splits aren't actually in the XML export. The Wesabeans saw the use of that when I mentioned it to them, so they're going to add it at some point in the future. Next up for me is to add some chartsengrafs to the output mix.

Friday, May 11, 2007

Jython 2.2b2 released!

In news from the all-consuming destroyer of free time front, I've just released the second beta towards Jython 2.2. Download it and tell us if anything is broken!

The changes aren't actually that dramatic as the first beta was almost feature complete for 2.2 and the beta users didn't uncover many bugs. We did manage to fix 30 bugs, complete the new-style class integration and add a cool new customizable object adaptation system.

There are only a couple small changes to make a release candidate, so it should appear in a couple weeks. As long as all goes well there we can make the release a week or so after that and have the first stable release of Jython in over five years. It's a testament to the power of the idea of Python on the JVM that a project as dormant as Jython is awakening from its slumber.

Tuesday, May 8, 2007

Wesabe Export Downloader

Wesabe has an excellent interface to sanitize and categorize your financial information, so I've been using it for the past couple months to keep track of the dollars around here. They also allow you to download all of your data in a tidy XML format that contains all of the tagging and naming information you've added through said excellent interface. Since the reports they offer are still pretty basic, I've taken to chopping up the XML a little in Python to produce more complicated views of where the money has gone month by month, category by category.

The tedium of logging into the site, clicking through to the export page and actually downloading the XML every time I wanted to produce an updated report started to wear on me a bit though. grabber.py is a dumb little script that takes your Wesabe username and password as arguments, logs into wesabe, and downloads the XML as accounts.xml. They also have a csv format which grabber.py will grab if you pass it 'csv' as the final argument.

I've got another script that actually does more interesting things like parsing the XML with elementtree and making reports. I'll probably polish it up in the next few days and post it here as well.

Sunday, May 6, 2007

Nailgun Your Way to a Quick Jython Startup

Charles Nutter just mentioned Nailgun on jruby-dev as a possible way to speed up jruby startup time. Turns out it works pretty well for jython too.

Regular Jython:
groves@muhngole ~/Desktop/nailgun-0.7.1
$ time java -jar d:/home/groves/dev/jython/trunk/dist/jython.jar -c 'print "hello"'
hello

real 0m2.390s
user 0m0.015s
sys 0m0.015s


Nailgunned Jython:
groves@muhngole ~/Desktop/nailgun-0.7.1
$ time ./ng.exe org.python.util.jython -c 'print "hello"'
hello

real 0m0.062s
user 0m0.015s
sys 0m0.015s


CPython:
groves@muhngole ~/dev/jython/trunk
$ time python -c 'print "hello"'
hello

real 0m0.093s
user 0m0.030s
sys 0m0.093s


So with Nailgun, Jython can print 'hello' more quickly than CPython itself. Unfortunately, much of the time of the regular Java invocation of Jython is spent initializing Jython so the first run of Jython from inside Nailgun is actually a little slower than a regular run. After that, all of the initialization is done inside of Nailgun's VM and things run pretty quickly as shown in the numbers above.

From within Nailgun's directory it's just a three step process to run Jython:
  1. java -jar nailgun-0.7.1.jar
  2. ./ng ng-cp jython.jar
  3. ./ng org.python.util.jython -c 'print "hello"'
Since Nailgun is just passing its command line arguments through to Jython, you can just pass the script you want Jython to run to ng instead of the -c option I'm using as an example.

Me

I'm also here and here.

Archive