Use Amount objects, use the XML output in bal()
This commit is contained in:
parent
dd260aa021
commit
af6e28d168
2 changed files with 62 additions and 21 deletions
|
@ -40,7 +40,6 @@ class Ledger:
|
||||||
_log.info('Waiting for one second... %d/%d', i, timeout)
|
_log.info('Waiting for one second... %d/%d', i, timeout)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
process = self.get_process()
|
process = self.get_process()
|
||||||
|
|
||||||
self.locked = True
|
self.locked = True
|
||||||
|
@ -111,25 +110,45 @@ class Ledger:
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def bal(self):
|
def bal(self):
|
||||||
output = self.send_command('bal --format "%A|%t\\\\n"')
|
output = self.send_command('xml')
|
||||||
|
|
||||||
if output is None:
|
if output is None:
|
||||||
raise RuntimeError('bal call returned no output')
|
raise RuntimeError('bal call returned no output')
|
||||||
|
|
||||||
accounts = []
|
accounts = []
|
||||||
|
|
||||||
for line in output.split(b'\n'):
|
xml = ElementTree.fromstring(output.decode('utf8'))
|
||||||
try:
|
|
||||||
name, balance = line.decode('utf8').split('|')
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
accounts.append(Account(name=name, balance=balance))
|
accounts = self._recurse_accounts(xml.find('./accounts'))
|
||||||
|
|
||||||
|
return accounts
|
||||||
|
|
||||||
|
def _recurse_accounts(self, root):
|
||||||
|
accounts = []
|
||||||
|
|
||||||
|
for account in root.findall('./account'):
|
||||||
|
name = account.find('./fullname').text
|
||||||
|
|
||||||
|
amounts = []
|
||||||
|
|
||||||
|
account_amounts = account.findall('./account-total/balance/amount') or \
|
||||||
|
account.findall('./account-amount/amount')
|
||||||
|
|
||||||
|
if account_amounts:
|
||||||
|
for amount in account_amounts:
|
||||||
|
quantity = amount.find('./quantity').text
|
||||||
|
symbol = amount.find('./commodity/symbol').text
|
||||||
|
|
||||||
|
amounts.append(Amount(amount=quantity, symbol=symbol))
|
||||||
|
|
||||||
|
accounts.append(Account(name=name,
|
||||||
|
amounts=amounts,
|
||||||
|
accounts=self._recurse_accounts(account)))
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
def reg(self):
|
def reg(self):
|
||||||
output = self.send_command( 'xml')
|
output = self.send_command('xml')
|
||||||
|
|
||||||
if output is None:
|
if output is None:
|
||||||
raise RuntimeError('reg call returned no output')
|
raise RuntimeError('reg call returned no output')
|
||||||
|
@ -152,7 +171,8 @@ class Ledger:
|
||||||
'./post-amount/amount/commodity/symbol').text
|
'./post-amount/amount/commodity/symbol').text
|
||||||
|
|
||||||
postings.append(
|
postings.append(
|
||||||
Posting(account=account, amount=amount, symbol=symbol))
|
Posting(account=account,
|
||||||
|
amount=Amount(amount=amount, symbol=symbol)))
|
||||||
|
|
||||||
entries.append(
|
entries.append(
|
||||||
Transaction(date=date, payee=payee, postings=postings))
|
Transaction(date=date, payee=payee, postings=postings))
|
||||||
|
@ -170,33 +190,44 @@ class Transaction:
|
||||||
return ('<{self.__class__.__name__} {date}' +
|
return ('<{self.__class__.__name__} {date}' +
|
||||||
' {self.payee} {self.postings}').format(
|
' {self.payee} {self.postings}').format(
|
||||||
self=self,
|
self=self,
|
||||||
date=self.date.isoformat())
|
date=self.date.strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
|
|
||||||
class Posting:
|
class Posting:
|
||||||
def __init__(self, account=None, amount=None, symbol=None):
|
def __init__(self, account=None, amount=None):
|
||||||
self.account = account
|
self.account = account
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<{self.__class__.__name__} "{self.account}"' +
|
||||||
|
' {self.amount}>').format(self=self)
|
||||||
|
|
||||||
|
|
||||||
|
class Amount:
|
||||||
|
def __init__(self, amount=None, symbol=None):
|
||||||
|
self.amount = amount
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ('<{self.__class__.__name__} "{self.account}"' +
|
return ('<{self.__class__.__name__} {self.symbol}' +
|
||||||
' {self.symbol} {self.amount}>').format(self=self)
|
' {self.amount}>').format(self=self)
|
||||||
|
|
||||||
|
|
||||||
class Account:
|
class Account:
|
||||||
def __init__(self, name=None, balance=None):
|
def __init__(self, name=None, amounts=None, accounts=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.balance = balance
|
self.amounts = amounts
|
||||||
|
self.accounts = accounts
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<{self.__class__.__name__}: "{self.name}" {self.balance} >'.format(
|
return ('<{self.__class__.__name__} "{self.name}" {self.amounts}' +
|
||||||
self=self)
|
' {self.accounts}>').format(self=self)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.INFO)
|
||||||
ledger = Ledger(ledger_file='non-profit-test-data.ledger')
|
ledger = Ledger(ledger_file='non-profit-test-data.ledger')
|
||||||
print(ledger.bal())
|
print(ledger.bal())
|
||||||
print(ledger.reg())
|
print(ledger.reg())
|
||||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
|
|
||||||
from flask import Flask, g, jsonify, json
|
from flask import Flask, g, jsonify, json
|
||||||
|
|
||||||
from accounting import Ledger, Account, Posting, Transaction
|
from accounting import Ledger, Account, Posting, Transaction, Amount
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
@ -11,12 +11,14 @@ app.config.from_pyfile('config.py')
|
||||||
|
|
||||||
ledger = Ledger(ledger_file=app.config['LEDGER_FILE'])
|
ledger = Ledger(ledger_file=app.config['LEDGER_FILE'])
|
||||||
|
|
||||||
|
|
||||||
class AccountingEncoder(json.JSONEncoder):
|
class AccountingEncoder(json.JSONEncoder):
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, Account):
|
if isinstance(o, Account):
|
||||||
return dict(
|
return dict(
|
||||||
name=o.name,
|
name=o.name,
|
||||||
balance=o.balance
|
amounts=o.amounts,
|
||||||
|
accounts=o.accounts
|
||||||
)
|
)
|
||||||
elif isinstance(o, Transaction):
|
elif isinstance(o, Transaction):
|
||||||
return dict(
|
return dict(
|
||||||
|
@ -28,23 +30,31 @@ class AccountingEncoder(json.JSONEncoder):
|
||||||
return dict(
|
return dict(
|
||||||
account=o.account,
|
account=o.account,
|
||||||
amount=o.amount,
|
amount=o.amount,
|
||||||
|
)
|
||||||
|
elif isinstance(o, Amount):
|
||||||
|
return dict(
|
||||||
|
amount=o.amount,
|
||||||
symbol=o.symbol
|
symbol=o.symbol
|
||||||
)
|
)
|
||||||
|
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
app.json_encoder = AccountingEncoder
|
app.json_encoder = AccountingEncoder
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return 'Hello World!'
|
return 'Hello World!'
|
||||||
|
|
||||||
|
|
||||||
@app.route('/balance')
|
@app.route('/balance')
|
||||||
def balance_report():
|
def balance_report():
|
||||||
report_data = ledger.bal()
|
report_data = ledger.bal()
|
||||||
|
|
||||||
return jsonify(balance_report=report_data)
|
return jsonify(balance_report=report_data)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/register')
|
@app.route('/register')
|
||||||
def register_report():
|
def register_report():
|
||||||
report_data = ledger.reg()
|
report_data = ledger.reg()
|
||||||
|
|
Loading…
Reference in a new issue