Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
278 lines (220 sloc) 8.55 KB
#!/usr/bin/env python
"""
generate invoices
Usage:
restful.py [-v] --config=<config> <invoices>
Parameters:
<invoices> set list of invoices.
-c,--config=<config> Setup config file.
-o,--output=<output> Directory to generate the files to. [default: ./]
-v,--verbose Set verbose output.
-h,--help This message.
-V,--version Show version.
"""
import os
import sys
import time
import operator
from flask import Flask, request, render_template, make_response, send_from_directory
from flask_restful import Resource, Api
from flask_webpack import Webpack
from flask.ext.cors import CORS, cross_origin
from subprocess import Popen
from functools import update_wrapper
from glob import glob
import jinja2
from . import invoice
def Template(app, path):
loader = jinja2.ChoiceLoader([
app.jinja_loader,
jinja2.FileSystemLoader(path),
])
app.jinja_loader = loader
def WebpackWatcher(app, webpack_config='./webpack.config.js'):
if 'WERKZEUG_RUN_MAIN' not in os.environ:
p = Popen(['node_modules/.bin/webpack',
'--config', webpack_config,
'--progress',
'--profile',
'--colors',
'--content-base', 'src/static',
'--inline',
'--watch' ]
)
st = time.time()
while time.time() - st < 20: # 20s timeout
if os.path.exists(app.config["WEBPACK_MANIFEST_PATH"]):
break
time.sleep(1)
else:
p.terminate()
print('Fatal error: Timeout waiting for {} to be generated by webpack!'.format(app.config["WEBPACK_MANIFEST_PATH"]))
sys.exit(-1)
return app
def build_api(acct, args=None):
root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
static_folder = os.path.join(root, 'static')
# Enable webpack asset tracking and availability
wp = Webpack()
app = Flask('pyinvoice', static_folder=static_folder)
app.config["REQUIREJS_BIN"] = os.path.join(root,
'..',
'node_modules',
'requirejs',
'bin',
'r.js')
app.config["REQUIREJS_CONFIG"] = os.path.join(root, 'build.js')
app.config["REQUIREJS_RUN_IN_DEBUG"] = False
app.config["WEBPACK_MANIFEST_PATH"] = os.path.join(root, 'manifest.json')
extra_files=[]
if args['--verbose']:
extra_files += [app.config["WEBPACK_MANIFEST_PATH"]]
Webpack(app)
WebpackWatcher(app, os.path.join(root, 'webpack.config.js'))
Template(app, 'src/templates')
# Enable CORS for the app
CORS(app, origins='*')
api = Api(app)
def parse_get_args(func):
def func_wrapper(*args, **kwarg):
kwarg['page'] = int(request.args.get('page', 0))
kwarg['per_page'] = int(request.args.get('per_page', -1))
kwarg['ordering'] = request.args.get('ordering', 'iid')
kwarg['search'] = request.args.get('search', None)
kwarg['format'] = request.args.get('format', None)
return func(*args, **kwarg)
return update_wrapper(func_wrapper, func)
class AccountResults(Resource):
def get(self):
return {
'yearly': acct.calculate_yearly(),
'quarterly': acct.calculate_quarterly(),
'monthly': acct.calculate_monthly(),
}
class CustomerList(Resource):
keys = [
'name',
'address'
]
@parse_get_args
def get(self, page, per_page, ordering, search, format):
customers = acct.customers.copy()
if search:
customers = filter(lambda x: search in x, customers)
if ordering:
reverse = False
if ordering.startswith('-'):
ordering = ordering
reverse = True
customers.sort(key=lambda x: getattr(x, ordering,
getattr(x, 'iid', '')),
reverse=reverse)
if 0 > int(per_page):
return customers
return customers[per_page*page:per_page*(page+1)]
class Customer(Resource):
def get(self, customer_id, action='show'):
customer = acct.get_customer(customer_id)
if not customer:
raise Exception("Customer {} not found.".format(customer_id))
return customer
class InvoiceList(Resource):
keys = [
'iid',
'kind',
'date',
'place',
'subject',
'description',
'customer',
'products'
]
@parse_get_args
def get(self, page, per_page, ordering, search, format):
invoices = acct.invoices.copy()
if search:
invoices = filter(lambda x: search in x, invoices)
if ordering:
reverse = True
if ordering.startswith('-'):
ordering = ordering
reverse = False
invoices.sort(key=lambda x: getattr(x, ordering,
getattr(x, 'iid', '')),
reverse=reverse)
if 0 > int(per_page):
return invoices
return invoices[per_page*page:per_page*(page+1)]
def post(self):
i = invoice.Invoice(**request.form['data'])
acct.append(i)
acct.save()
return i, 201
class Invoice(Resource):
def get(self, invoice_id, action='show'):
if invoice_id.startswith("IV"):
invoice_id = invoice_id[2:]
invoice = acct.get_invoice(invoice_id)
if not invoice:
raise Exception("Invoice {} not found.".format(invoice_id))
if action == 'show':
return invoice
elif action == 'download':
acct.generate_pdf()
return send_from_directory(acct._output,
'IV{}.pdf'.format(invoice_id))
def put(self, invoice_id):
invoice = acct.get_invoice(invoice_id)
if not invoice:
raise Exception("Invoice {} not found.".format(invoice_id))
data = request.form['data']
for key, value in data.items():
if not getattr(invoice, key):
raise Exception("Key {} not found in {}".format(key, invoice))
else:
setattr(invoice, key, value)
acct.save()
return invoice
def post(self, invoice_id):
invoice = Invoice(**request.form['data'])
acct.append(invoice)
acct.save()
return invoice
api.add_resource(AccountResults, '/results')
api.add_resource(InvoiceList, '/invoices', )
api.add_resource(Invoice,
'/invoices/<string:invoice_id>',
'/invoices/<string:invoice_id>/<string:action>')
api.add_resource(CustomerList, '/customers', )
api.add_resource(Customer,
'/customers/<string:invoice_id>',
'/customers/<string:invoice_id>/<string:action>')
@app.route('/')
def basic_pages(**kwargs):
return render_template('index.html')
#return make_response(open(os.path.join(app.root_path,
# 'src/static/index.html')).read())
@app.route("/assets/<path:filename>")
def send_asset(filename):
print("YYY", os.path.join(root, 'static'), filename)
return send_from_directory(os.path.join(root, 'static'), filename)
# @app.route('/scripts/<path:script_path>')
# def scripts(script_path, **kwargs):
# return send_from_directory('src/static/scripts', script_path)
# @app.route('/images/<img>')
# def images(img, **kwargs):
# return send_from_directory('src/static/images', img)
# @app.route('/styles/<style>')
# def styles(style, **kwargs):
# return send_from_directory('src/static/styles', style)
app.run(debug=args['--verbose'],
extra_files=extra_files)
if __name__ == '__main__':
args = docopt.docopt(__doc__)
acct = Accounting(
config=args['--config'],
verbose=args['--verbose'],
output=args['--output']
)
acct.load(args['<invoices>'])
build_api()