Skip to content

Instantly share code, notes, and snippets.

@dzg
Last active August 3, 2021 17:34
Show Gist options
  • Save dzg/c208731bbb1bfff2e1c3e0385a51448d to your computer and use it in GitHub Desktop.
Save dzg/c208731bbb1bfff2e1c3e0385a51448d to your computer and use it in GitHub Desktop.
Grab personal financial data from buxfer.com and format for xbar (https://github.com/matryer/xbar)
#!/usr/local/bin/python3
import urllib.request as urllib2
import sys
import simplejson
from datetime import datetime
import time
# Get a token from buxfer:
# https://www.buxfer.com/api/login?userid={your user ID}&password={your password}
# Optionally, https://www.buymeacoffee.com/dzeevg
token = "{insert token from above URL}"
auth = "&token=" + token
base = "https://www.buxfer.com/api/"
format = ' | font=menlo trim=false '
nf1 = '10,.2f'
now = datetime.now()
def req(response):
result = simplejson.load(response)
response = result['response']
if response['status'] != "OK":
print ("An error occured: ", response['status'].replace('ERROR: ', ''))
sys.exit(1)
return response
def get(cmd,arg=''):
url = base + cmd + '?' + arg + auth
timeout = 3 # seconds to wait between failed API calls
while True:
try:
return req(urllib2.urlopen(urllib2.Request(url)))[cmd]
break
except:
time.sleep(timeout)
continue
def num(n): return f"{n:10,.2f}"
# You might need to add more color definitions here depending on the transaction types you have
colors = {
'expense': '#FF0000',
'income': '#00FF00',
'transfer': '#FFAA00',
'refund': '#00FFAA',
'investment purchase': '#FFFFAA',
'+': '#00FF00',
'-': '#FF0000',
'0': '#333333'
}
def printTrans(trans,prefix='',size=12,accts=False,width=7):
for tran in trans:
amt = tran['amount']; type = tran['type']
if type == 'expense': amt=-amt
print(prefix, tran['date'], tran['accountName'][0:width].ljust(width) if accts else '',
num(amt), tran['description'], format + f'size={str(size)}' + f' color={colors[type]}')
accts = get('accounts')
# You might need to add more type definitions here depending on the accounts types you have
for acct in accts: acct['type'] = next(
(type for type in
[
{'type':'checking', 'accts':['Schwab','Venmo']},
{'type':'credit', 'accts':['Banana','CapitalOne']},
{'type':'other', 'accts':['Geico','PayPal']},
{'type':'invest', 'accts':['Schwab Brokerage','401K','Coinbase']}
]
if acct['name'] in type['accts']),
{'type':'other'} # This is the default for any accounts not listed above
)['type']
# Sort the accounts by type, and ignore some
accts = [acct for acct in sorted(accts, key=lambda k: k['type'])
if acct['name'] not in ['PayPal GBP','PayPal ILS','PayPal EUR']] # these are filtered out
print('$',num(sum(acct['balance'] for acct in accts))) # Net worth (ish) at the top
print ("---")
lasttype = accts[0]['type']
for acct in accts:
if acct['type'] != lasttype:
lasttype = acct['type']
print ("---")
bal = acct['balance']; name = acct['name']
lastSynced = datetime.strptime(acct['lastSynced'], '%Y-%m-%d %H:%M:%S')
age = (now-lastSynced).days
print(num(bal), name, f'(•{age})' if age>0 else '', format,
f"href=https://www.buxfer.com/account?id={acct['id']}",
f"color={colors['0' if bal==0 else str(f'{bal:+}')[0]]}")
printTrans(get('transactions','accountName='+name.replace(' ','%20')),'--',12)
print(f'--{age} days ({lastSynced}) | size=10')
print ("---")
# 2 pages of recent transactions from all accounts:
printTrans(get('transactions'),'',10,accts=True)
printTrans(get('transactions','page=2'),'',10,accts=True) # Delete this if 1 page is enough for you
@dzg
Copy link
Author

dzg commented Aug 3, 2021

If you like this, https://www.buymeacoffee.com/dzeevg ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment