-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmoney_flow.py
executable file
·136 lines (110 loc) · 3.79 KB
/
money_flow.py
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python
# fuck you python; why make it so hard to cleanly import from parent dir?
from os.path import dirname, abspath
import sys ; sys.path.insert(1, dirname(dirname(abspath(__file__))))
import math
import os
import subprocess
import time
import fuckometer
# try to increase worth by at least this much each month
monthly_goal = 1000
beanfile = '%s/bank/bean/selene.bean' % (os.environ['HOME'])
datefmt = '%Y-%m-%d'
def main(args):
fuckometer.init()
cashflow = Beancount(condition=fuckometer.six_am)
if args: # just run once, print info, and exit
cashflow.update()
print(cashflow.text)
f = cashflow.fucks()
print('Fucks: %.2f' % (f))
else:
cashflow.loop() # run forever
class Beancount(fuckometer.Factor):
"""Money flow gauge from Beancount
Gauges monetary situation based on cash flow for the most-recent month on
record.
"""
path = 'money_flow'
def fucks(self):
adjusted = self.raw - monthly_goal
balance_fucks = 100.0 * (2.0 - math.log(max(1,self.raw2-20000), 500))
if adjusted > 0:
flow_fucks = -math.sqrt(adjusted)
else:
flow_fucks = math.sqrt(-adjusted)
if fuckometer.cfg.verbose:
print('balance_fucks: %s' % balance_fucks)
print('flow_fucks: %s' % flow_fucks)
fucks = balance_fucks + flow_fucks
return fucks
def on_update(self):
if fuckometer.cfg.verbose:
print(self.text)
print('Fucks: %s' % self.fucks())
def update(self):
self.raw = self.flow_last_month()
self.raw2 = self.current_balance
sign = '+'
if self.raw < 0: sign = '-'
amt = abs(self.raw)
self.text = 'Money: %s$%.2f / M' % (sign, amt)
def detect_start_date(self):
"""Return a date one month before the last recorded transaction.
"""
# returns all transactions in order
cmd = ('bean-query', beanfile, 'select *;')
err, text = run(cmd)
text = text[-1000:]
lines = text.split('\n')
lines = [l for l in lines if l.strip()]
parts = lines[-1].split()
date = parts[0]
when = list(time.strptime(date, datefmt))
when[1] -= 1 # one month ago
when = time.localtime(time.mktime(when))
#print(when)
return when
def flow_last_month(self):
begin = self.detect_start_date()
begin = time.strftime(datefmt, begin)
before = self.balance(begin)
after = self.balance_now()
flow = after - before
return flow
def balance(self, when):
cmd = ('bean-query', beanfile,
'balances at cost from close on %s where account ~ "Assets" and currency="USD";' % (when,))
err, text = run(cmd)
#print(text)
lines = [l.strip() for l in text.split('\n') if ' USD' in l]
#print(lines)
total = 0.0
for line in lines:
parts = line.split()
amount = float(parts[1])
total += amount
return total
def balance_now(self):
when = time.strftime(datefmt)
balance = self.balance(when)
self.current_balance = balance
if fuckometer.cfg.verbose:
print('balance_now(): %s' % balance)
return balance
def run(cmd):
"""Execute a command (tuple), return its errcode and text output"""
err = 0
text = ''
# catches stdout+stderr+retcode
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, close_fds=True)
# wait for process to finish and get its output
stdout, foo = p.communicate()
text = stdout.decode()
err = p.returncode
return err, text
if __name__ == "__main__":
import sys
main(sys.argv[1:])