Permalink
Browse files

adding basic graphing feature

  • Loading branch information...
1 parent 8cb23d3 commit 51c30451560d5684d6d1f8c4c72b80636806dcf5 @buggi22 committed Jan 10, 2012
Showing with 60 additions and 23 deletions.
  1. +45 −12 finance.py
  2. +1 −0 static/style.css
  3. +14 −11 templates/show_entries.html
View
57 finance.py
@@ -1,7 +1,9 @@
-# all the imports
+# imports
import sqlite3
+import StringIO
+import pylab
from flask import Flask, request, session, g, redirect, url_for, \
- abort, render_template, flash
+ abort, render_template, flash, make_response
# configuration
DATABASE = '/tmp/finance.db'
@@ -10,11 +12,10 @@
USERNAME = 'admin'
PASSWORD = 'default'
-# create our little application :)
app = Flask(__name__)
app.config.from_object(__name__)
-app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+app.config.from_envvar('FINANCE_APP_SETTINGS', silent=True)
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
@@ -27,8 +28,7 @@ def before_request():
def teardown_request(exception):
g.db.close()
-@app.route('/')
-def show_entries():
+def get_entries():
cur = g.db.execute('select description, amountcents, srcbucketname, srcbucketid, destbucketname, destbucketid, entryid from entries_labeled')
entries = [dict(description=row[0], amountstring=cents_to_string(row[1]), srcbucket=str(row[2]), srcbucketid=row[3], destbucket=str(row[4]), destbucketid=row[5], entryid=row[6]) for row in cur.fetchall()]
@@ -45,12 +45,17 @@ def show_entries():
entries[i / numinternals]['internals'] = []
entries[i / numinternals]['balances'] = []
change_string = cents_to_string( int(row[2]) ) if row[2] <> 0 else "-"
- entries[i / numinternals]['internals'] += [change_string]
+ entries[i / numinternals]['internals'] += [ change_string ]
runningtotals[i % numinternals] += row[2]
entries[i / numinternals]['balances'] += [ cents_to_string( int(runningtotals[i % numinternals]) ) ]
row = cur.fetchone()
i += 1
+ return (entries, internals)
+
+@app.route('/')
+def show_entries():
+ entries, internals = get_entries()
return render_template('show_entries.html', entries=entries, internals=internals)
@app.route('/add_entry', methods=['POST'])
@@ -99,9 +104,34 @@ def add_bucket():
flash('New bucket was successfully added')
return redirect(url_for('show_buckets'))
-def bucketname_to_int(name):
- return g.db.execute('select bucketid from buckets where bucketname = ?', [name]) \
- .fetchall()[0][0]
+@app.route('/history.png')
+def history_png():
+ entries, internals = get_entries()
+
+ xvalues = pylab.arange(0, len(entries)+1, 1)
+ yvalues = [[internal['initialbalancecents'] / 100.0] for internal in internals]
+
+ for e in entries:
+ for i, balance in enumerate(e['balances']):
+ yvalues[i] += [string_to_cents(balance) / 100.0]
+
+ series = []
+ for yv in yvalues:
+ series += [xvalues, yv]
+
+ pylab.clf() # clear current figure
+ pylab.plot(*series)
+ seriesnames = [internal['bucketname'] for internal in internals]
+ pylab.legend(seriesnames, 'lower right')
+
+ imgdata = StringIO.StringIO()
+ pylab.savefig(imgdata, format='png')
+ imgdata.seek(0)
+
+ response = make_response( imgdata.read() )
+ response.mimetype = 'image/png'
+
+ return response
@app.route('/login', methods=['GET', 'POST'])
def login():
@@ -123,14 +153,17 @@ def logout():
flash('You were logged out')
return redirect(url_for('show_entries'))
+def bucketname_to_int(name):
+ return g.db.execute('select bucketid from buckets where bucketname = ?', [name]) \
+ .fetchall()[0][0]
+
def cents_to_string(cents):
sign = ''
if(cents == None):
return 'None'
elif(cents < 0):
- cents = -cents
sign = '-'
- return sign + "$%d.%02d" % (int(cents/100), cents % 100)
+ return sign + "$%d.%02d" % (int(abs(cents)/100), abs(cents) % 100)
def string_to_cents(s):
multiplier = 1
View
1 static/style.css
@@ -9,6 +9,7 @@ h2 { font-size: 1.2em; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
+.entries td { padding: 0.5em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
View
25 templates/show_entries.html
@@ -18,42 +18,45 @@
<a href="{{ url_for('show_buckets') }}">Show Buckets</a>
- <table>
+ <img src="{{ url_for('history_png') }}" />
+
+ <table class="entries">
<tr>
- <td>Amount</td>
- <td>From</td>
- <td>To</td>
- <td>&nbsp;</td>
+ <th>Amount</th>
+ <th>From</th>
+ <th>To</th>
+ <th>&nbsp;</th>
{% for internal in internals %}
- <td>{{ internal.bucketname }}</td>
+ <th>{{ internal.bucketname }}</th>
{% endfor %}
</tr>
<tr class=resultingbalance>
<td colspan=3>&nbsp;</td>
<td>Initial balance: </td>
{% for internal in internals %}
- <td>{{ internal.initialbalancestring }}</td>
+ <td align="right">{{ internal.initialbalancestring }}</td>
{% endfor %}
</tr>
{% for entry in entries %}
<tr>
- <td>{{ entry.amountstring }}</td>
+ <td align="right">{{ entry.amountstring }}</td>
<td>{{ entry.srcbucket }}</td>
<td>{{ entry.destbucket }}</td>
<td>Change in balance:</td>
{% for internal in entry.internals %}
- <td>{{ internal }}</td>
+ <td align="right">{{ internal }}</td>
{% endfor %}
</tr>
<tr class=resultingbalance>
<td colspan=3>{{ entry.description }}&nbsp;</td>
<td>Resulting balance:</td>
{% for balance in entry.balances %}
- <td>{{ balance }}</td>
+ <td align="right">{{ balance }}</td>
{% endfor %}
</tr>
{% else %}
- <li><em>Unbelievable. No entries here so far</em>
+ <tr><td><em>Unbelievable. No entries here so far</em></td></tr>
{% endfor %}
+ <!-- TODO: put new-entry form at bottom row of table -->
</table>
{% endblock %}

0 comments on commit 51c3045

Please sign in to comment.