Last active
August 3, 2021 17:34
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you like this, https://www.buymeacoffee.com/dzeevg ❤️