Changeset - 6f2c875c7b76
[Not reviewed]
0 3 2
Joar Wandborg - 10 years ago 2013-12-10 23:25:16
joar@wandborg.se
Added /transaction endpoint
5 files changed with 125 insertions and 2 deletions:
0 comments (0 inline, 0 general)
accounting/__init__.py
Show inline comments
...
 
@@ -111,6 +111,30 @@ class Ledger:
 

	
 
            return output
 

	
 
    def add_transaction(self, transaction):
 
        transaction_template = ('\n{date} {t.payee}\n'
 
                                '{postings}')
 

	
 
        posting_template = ('  {account} {p.amount.symbol}'
 
                            ' {p.amount.amount}\n')
 

	
 
        output  = b''
 

	
 
        output += transaction_template.format(
 
            date=transaction.date.strftime('%Y-%m-%d'),
 
            t=transaction,
 
            postings=''.join([posting_template.format(
 
                p=p,
 
                account=p.account + ' ' * (
 
                    80 - (len(p.account) + len(p.amount.symbol) +
 
                    len(p.amount.amount) + 1 + 2)
 
                )) for p in transaction.postings])).encode('utf8')
 

	
 
        with open(self.ledger_file, 'ab') as f:
 
            f.write(output)
 

	
 
        _log.debug('written to file: %s', output)
 

	
 
    def bal(self):
 
        output = self.send_command('xml')
 

	
accounting/decorators.py
Show inline comments
 
new file 100644
 
from functools import wraps
 

	
 
from flask import jsonify
 

	
 
from accounting.exceptions import AccountingException
 

	
 

	
 
def jsonify_exceptions(func):
 
    '''
 
    Wraps a Flask endpoint and catches any AccountingException-based
 
    exceptions which are returned to the client as JSON.
 
    '''
 
    @wraps(func)
 
    def wrapper(*args, **kw):
 
        try:
 
            return func(*args, **kw)
 
        except AccountingException as exc:
 
            return jsonify(error=exc)
 

	
 
    return wrapper
accounting/exceptions.py
Show inline comments
 
new file 100644
 
class AccountingException(Exception):
 
    '''
 
    Used as a base for exceptions that are returned to the caller via the
 
    jsonify_exceptions decorator
 
    '''
 
    pass
accounting/transport.py
Show inline comments
 
from datetime import datetime
 

	
 
from flask import json
 

	
 
from accounting import Amount, Transaction, Posting, Account
...
 
@@ -30,6 +32,11 @@ class AccountingEncoder(json.JSONEncoder):
 
                amount=o.amount,
 
                symbol=o.symbol
 
            )
 
        elif isinstance(o, Exception):
 
            return dict(
 
                __type__=o.__class__.__name__,
 
                args=o.args
 
            )
 

	
 
        return json.JSONEncoder.default(self, o)
 

	
...
 
@@ -46,4 +53,7 @@ class AccountingDecoder(json.JSONDecoder):
 

	
 
        _type = d.pop('__type__')
 

	
 
        if _type == 'Transaction':
 
            d['date'] = datetime.strptime(d['date'], '%Y-%m-%d')
 

	
 
        return types[_type](**d)
accounting/web.py
Show inline comments
...
 
@@ -2,10 +2,12 @@ import sys
 
import logging
 
import argparse
 

	
 
from flask import Flask, g, jsonify, json, request
 
from flask import Flask, jsonify, request
 

	
 
from accounting import Ledger, Account, Posting, Transaction, Amount
 
from accounting import Ledger
 
from accounting.transport import AccountingEncoder, AccountingDecoder
 
from accounting.exceptions import AccountingException
 
from accounting.decorators import jsonify_exceptions
 

	
 

	
 
app = Flask('accounting')
...
 
@@ -32,6 +34,67 @@ def balance_report():
 
    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'''
0 comments (0 inline, 0 general)