172 lines
5 KiB
Python
172 lines
5 KiB
Python
import sys
|
|
import logging
|
|
import argparse
|
|
|
|
from flask import Flask, jsonify, request
|
|
|
|
from accounting import Ledger
|
|
from accounting.transport import AccountingEncoder, AccountingDecoder
|
|
from accounting.exceptions import AccountingException
|
|
from accounting.decorators import jsonify_exceptions
|
|
|
|
|
|
app = Flask('accounting')
|
|
app.config.from_pyfile('config.py')
|
|
|
|
ledger = Ledger(ledger_file=app.config['LEDGER_FILE'])
|
|
|
|
|
|
# These will convert output from our internal classes to JSON and back
|
|
app.json_encoder = AccountingEncoder
|
|
app.json_decoder = AccountingDecoder
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return 'Hello World!'
|
|
|
|
|
|
@app.route('/balance')
|
|
def balance_report():
|
|
''' Returns the balance report from ledger '''
|
|
report_data = ledger.bal()
|
|
|
|
return jsonify(balance_report=report_data)
|
|
|
|
|
|
@app.route('/transaction', methods=['POST'])
|
|
@jsonify_exceptions
|
|
def transaction():
|
|
'''
|
|
REST/JSON endpoint for transactions.
|
|
|
|
Current state:
|
|
|
|
Takes a POST request with a ``transactions`` JSON payload and writes it to
|
|
the ledger file.
|
|
|
|
Requires the ``transactions`` payload to be __type__-annotated:
|
|
|
|
.. code-block:: json
|
|
|
|
{
|
|
"transactions": [
|
|
{
|
|
"__type__": "Transaction",
|
|
"date": "2013-01-01",
|
|
"payee": "Kindly T. Donor",
|
|
"postings": [
|
|
{
|
|
"__type__": "Posting",
|
|
"account": "Income:Foo:Donation",
|
|
"amount": {
|
|
"__type__": "Amount",
|
|
"amount": "-100",
|
|
"symbol": "$"
|
|
}
|
|
},
|
|
{
|
|
"__type__": "Posting",
|
|
"account": "Assets:Checking",
|
|
"amount": {
|
|
"__type__": "Amount",
|
|
"amount": "100",
|
|
"symbol": "$"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
becomes::
|
|
|
|
2013-01-01 Kindly T. Donor
|
|
Income:Foo:Donation $ -100
|
|
Assets:Checking $ 100
|
|
'''
|
|
transactions = request.json.get('transactions')
|
|
|
|
if not transactions:
|
|
raise AccountingException('No transaction data provided')
|
|
|
|
for transaction in transactions:
|
|
ledger.add_transaction(transaction)
|
|
|
|
return jsonify(foo='bar')
|
|
|
|
|
|
@app.route('/parse-json', methods=['POST'])
|
|
def parse_json():
|
|
r'''
|
|
Parses a __type__-annotated JSON payload and debug-logs the decoded version
|
|
of it.
|
|
|
|
Example:
|
|
|
|
wget http://127.0.0.1:5000/balance -O balance.json
|
|
curl -X POST -H 'Content-Type: application/json' -d @balance.json \
|
|
http://127.0.0.1/parse-json
|
|
# Logging output (linebreaks added for clarity)
|
|
DEBUG:accounting:json data: {'balance_report':
|
|
[<Account "None" [
|
|
<Amount $ 0>, <Amount KARMA 0>]
|
|
[<Account "Assets" [
|
|
<Amount $ 50>, <Amount KARMA 10>]
|
|
[<Account "Assets:Checking" [
|
|
<Amount $ 50>] []>,
|
|
<Account "Assets:Karma Account" [
|
|
<Amount KARMA 10>] []>]>,
|
|
<Account "Expenses" [
|
|
<Amount $ 500>]
|
|
[<Account "Expenses:Blah" [
|
|
<Amount $ 250>]
|
|
[<Account "Expenses:Blah:Hosting" [
|
|
<Amount $ 250>] []>]>,
|
|
<Account "Expenses:Foo" [
|
|
<Amount $ 250>] [
|
|
<Account "Expenses:Foo:Hosting" [
|
|
<Amount $ 250>] []>]>]>,
|
|
<Account "Income" [
|
|
<Amount $ -550>,
|
|
<Amount KARMA -10>]
|
|
[<Account "Income:Donation" [
|
|
<Amount $ -50>] []>,
|
|
<Account "Income:Foo" [
|
|
<Amount $ -500>]
|
|
[<Account "Income:Foo:Donation" [
|
|
<Amount $ -500>] []>]>,
|
|
<Account "Income:Karma" [
|
|
<Amount KARMA -10>] []>]>]>]}
|
|
'''
|
|
app.logger.debug('json data: %s', request.json)
|
|
return jsonify(foo='bar')
|
|
|
|
|
|
@app.route('/register')
|
|
def register_report():
|
|
''' Returns the register report from ledger '''
|
|
report_data = ledger.reg()
|
|
|
|
return jsonify(register_report=report_data)
|
|
|
|
|
|
def main(argv=None):
|
|
prog = __name__
|
|
if argv is None:
|
|
prog = sys.argv[0]
|
|
argv = sys.argv[1:]
|
|
|
|
parser = argparse.ArgumentParser(prog=prog)
|
|
parser.add_argument('-v', '--verbosity',
|
|
default='INFO',
|
|
help=('Filter logging output. Possible values:' +
|
|
' CRITICAL, ERROR, WARNING, INFO, DEBUG'))
|
|
|
|
args = parser.parse_args(argv)
|
|
|
|
logging.basicConfig(level=getattr(logging, args.verbosity, 'INFO'))
|
|
|
|
app.run(host=app.config['HOST'], port=app.config['PORT'])
|
|
|
|
if __name__ == '__main__':
|
|
main()
|