summaryrefslogtreecommitdiff
path: root/animism-align/cli/app/server/decorators.py
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2020-06-23 23:18:07 +0200
committerJules Laplace <julescarbon@gmail.com>2020-06-23 23:18:07 +0200
commit3cf70771cb45cc16ec33ffe44e7a1a4799d8f395 (patch)
tree55f0edb53141d5f043b486d722f507bfd94abdea /animism-align/cli/app/server/decorators.py
parent014816dc724c1be60b7dd28d4e608c89b4ed451c (diff)
adding web app base
Diffstat (limited to 'animism-align/cli/app/server/decorators.py')
-rw-r--r--animism-align/cli/app/server/decorators.py127
1 files changed, 127 insertions, 0 deletions
diff --git a/animism-align/cli/app/server/decorators.py b/animism-align/cli/app/server/decorators.py
new file mode 100644
index 0000000..2e6f9dd
--- /dev/null
+++ b/animism-align/cli/app/server/decorators.py
@@ -0,0 +1,127 @@
+"""
+These decorator functions wrap APIs for the simple search server.
+"""
+
+import os
+from time import time
+from datetime import datetime
+import numpy as np
+from PIL import Image
+from flask import request, jsonify
+from werkzeug.utils import secure_filename
+
+from app.sql.common import Session
+
+DEFAULT_LIMIT = 30
+
+def api_query(f):
+ """Wrap basic API queries with timing"""
+ def wrap_api_query(*args, **kwargs):
+ start = time()
+ query = {}
+ kwargs['query'] = query
+ results = f(*args, **kwargs)
+ query['timing'] = round(time() - start, 2)
+ if 'crop' in kwargs['query'] and kwargs['query']['crop'] is None:
+ del kwargs['query']['crop']
+ else:
+ crop = kwargs['query']['crop']
+ kwargs['query']['crop'] = {
+ 'x': crop[0],
+ 'y': crop[1],
+ 'w': crop[2],
+ 'h': crop[3],
+ }
+ if isinstance(results, list):
+ return { 'query': query, 'res': results }
+ else:
+ return { 'query': query, 'res': results }
+ wrap_api_query.__name__ = f.__name__
+ return wrap_api_query
+
+def db_session(f):
+ """Wrap API queries in a database session object which gets committed"""
+ def wrap_db_session(*args, **kwargs):
+ session = Session()
+ try:
+ kwargs['session'] = session
+ f(*args, **kwargs)
+ session.commit()
+ except:
+ session.rollback()
+ raise
+ finally:
+ session.close()
+ wrap_db_session.__name__ = f.__name__
+ return wrap_db_session
+
+def get_offset_and_limit(f):
+ """Normalize offset/limit query string params"""
+ def wrap_offset(*args, **kwargs):
+ kwargs['query']['offset'] = request.args.get('offset', default=0, type=int)
+ kwargs['query']['limit'] = request.args.get('limit', default=DEFAULT_LIMIT, type=int)
+ x = float(request.args.get('x', default=0.0, type=float))
+ y = float(request.args.get('y', default=0.0, type=float))
+ w = float(request.args.get('w', default=0.0, type=float))
+ h = float(request.args.get('h', default=0.0, type=float))
+ if w != 0.0 and h != 0.0:
+ kwargs['query']['crop'] = (x, y, w, h,)
+ else:
+ kwargs['query']['crop'] = None
+ return f(*args, **kwargs)
+ wrap_offset.__name__ = f.__name__
+ return wrap_offset
+
+def store_uploaded_image(param_name, store=False, uploaded_im_path='static/data/uploaded'):
+ """Retrive an uploaded image and prepare for processing. Optionally store it to disk."""
+ def decorator(f):
+ def wrap_uploaded_image(*args, **kwargs):
+ if param_name not in request.files:
+ raise APIError('No file uploaded')
+
+ file = request.files[param_name]
+
+ # convert string of image data to uint8
+ nparr = np.fromstring(file.read(), np.uint8)
+
+ # decode image
+ im = Image.fromarray(nparr)
+ kwargs['im'] = im
+
+ if store:
+ uploaded_im_fn = secure_filename(datetime.now().isoformat() + "_" + file.filename)
+ uploaded_im_abspath = os.path.join(uploaded_im_path, uploaded_im_fn)
+ uploaded_im_remote_path = os.path.join('/', uploaded_im_path, uploaded_im_fn)
+ nparr.tofile(uploaded_im_abspath)
+ kwargs['query']['url'] = uploaded_im_remote_path
+ return f(*args, **kwargs)
+ wrap_uploaded_image.__name__ = f.__name__
+ return wrap_uploaded_image
+ return decorator
+
+def as_json(f):
+ """Output an API query as JSON"""
+ def wrap_jsonify(*args, **kwargs):
+ return jsonify(f(*args, **kwargs))
+ wrap_jsonify.__name__ = f.__name__
+ return wrap_jsonify
+
+def exception_handler(f):
+ """Handle exceptions caused by the API"""
+ def wrapper(*args, **kwargs):
+ try:
+ return f(*args, **kwargs)
+ except Exception as e:
+ return {
+ "error": True,
+ "message": e.message,
+ "query": kwargs['query'],
+ }
+ wrapper.__name__ = f.__name__
+ return wrapper
+
+class APIError(Exception):
+ def __init__(self, message):
+ self.message = message
+ def __str__(self):
+ return repr(self.message)