aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron LI <aaronly.me@outlook.com>2016-11-22 17:55:21 +0800
committerAaron LI <aaronly.me@outlook.com>2016-11-22 17:55:21 +0800
commitd1e3f6219e1325e32e94de2b0c8189647c3393cb (patch)
tree4d920777e6c5e118b2af66896d93ec3782eae1d0
parentbc77803e4d72ec51f7c57ac09c2e131247adccaa (diff)
downloadfg21sim-d1e3f6219e1325e32e94de2b0c8189647c3393cb.tar.bz2
webui: Add "ProductsAJAXHandler" to manipulate the products manifest
TODO: some actions currently not implemented yet
-rw-r--r--fg21sim/webui/app.py6
-rw-r--r--fg21sim/webui/handlers/__init__.py1
-rw-r--r--fg21sim/webui/handlers/products.py173
3 files changed, 180 insertions, 0 deletions
diff --git a/fg21sim/webui/app.py b/fg21sim/webui/app.py
index ba27222..972771c 100644
--- a/fg21sim/webui/app.py
+++ b/fg21sim/webui/app.py
@@ -18,9 +18,11 @@ from .handlers import (IndexHandler,
LoginHandler,
ConfigsAJAXHandler,
ConsoleAJAXHandler,
+ ProductsAJAXHandler,
WSHandler)
from .utils import gen_cookie_secret
from ..configs import ConfigManager
+from ..products import Products
# Each module defines its own options, which are added to the global namespace
@@ -52,6 +54,8 @@ class Application(tornado.web.Application):
2. running=False, finished=True: finished
3. running=True, finished=False: running
4. running=True, finished=True: ?? error ??
+ products : `~fg21sim.products.Products`
+ Manage and manipulate the simulation products
"""
def __init__(self, **kwargs):
@@ -59,12 +63,14 @@ class Application(tornado.web.Application):
self.websockets = set()
self.executor = ThreadPoolExecutor(max_workers=options.max_workers)
self.task_status = {"running": False, "finished": False}
+ self.products = Products()
# URL handlers
handlers = [
url(r"/", IndexHandler, name="index"),
url(r"/login", LoginHandler, name="login"),
url(r"/ajax/configs", ConfigsAJAXHandler),
url(r"/ajax/console", ConsoleAJAXHandler),
+ url(r"/ajax/products", ProductsAJAXHandler),
url(r"/ws", WSHandler),
]
if options.debug:
diff --git a/fg21sim/webui/handlers/__init__.py b/fg21sim/webui/handlers/__init__.py
index 6fa4152..a859e6e 100644
--- a/fg21sim/webui/handlers/__init__.py
+++ b/fg21sim/webui/handlers/__init__.py
@@ -5,4 +5,5 @@ from .index import IndexHandler
from .login import LoginHandler
from .configs import ConfigsAJAXHandler
from .console import ConsoleAJAXHandler
+from .products import ProductsAJAXHandler
from .websocket import WSHandler
diff --git a/fg21sim/webui/handlers/products.py b/fg21sim/webui/handlers/products.py
new file mode 100644
index 0000000..5c9e0c8
--- /dev/null
+++ b/fg21sim/webui/handlers/products.py
@@ -0,0 +1,173 @@
+# Copyright (c) 2016 Weitian LI <liweitianux@live.com>
+# MIT license
+
+"""
+Handle the AJAX requests from the client to manage the simulation products.
+"""
+
+
+import os
+import logging
+
+import tornado.ioloop
+from tornado.escape import json_decode, json_encode
+
+from .base import BaseRequestHandler
+
+
+logger = logging.getLogger(__name__)
+
+
+class ProductsAJAXHandler(BaseRequestHandler):
+ """
+ Handle the AJAX requests from the client to manage the simulation products.
+
+ Attributes
+ ----------
+ from_localhost : bool
+ ``True`` if the request is from the localhost, otherwise ``False``.
+ """
+ def initialize(self):
+ """Hook for subclass initialization. Called for each request."""
+ self.products = self.application.products
+ if self.request.remote_ip == "127.0.0.1":
+ self.from_localhost = True
+ else:
+ self.from_localhost = False
+
+ def get(self):
+ """
+ Handle the READ-ONLY products manifest manipulations.
+
+ Supported actions:
+ - get: Get the current products manifest
+ - download: TODO
+ - open: TODO
+ """
+ action = self.get_argument("action", "get")
+ if action == "get":
+ response = {
+ "manifest": self.products.manifest,
+ "localhost": self.from_localhost,
+ }
+ success = True
+ else:
+ # ERROR: bad action
+ success = False
+ reason = "Bad request action: {0}".format(action)
+ #
+ if success:
+ logger.debug("Response: {0}".format(response))
+ self.set_header("Content-Type", "application/json; charset=UTF-8")
+ self.write(json_encode(response))
+ else:
+ logger.warning("Request failed: {0}".format(reason))
+ self.send_error(400, reason=reason)
+
+ @tornado.web.authenticated
+ def post(self):
+ """
+ Handle the READ-WRITE products manifest manipulations.
+
+ Supported actions:
+ - load: Load the products manifest from file
+ - save: Save the current products manifest to file
+ - reset: Reset existing products manifest
+ - convert: Convert the product from HEALPix map to HPX image
+ """
+ request = json_decode(self.request.body)
+ logger.debug("Received request: {0}".format(request))
+ action = request.get("action")
+ if action == "load":
+ # Load the manifest from supplied file
+ try:
+ success, reason = self._load_products(request["manifestfile"])
+ except KeyError:
+ success = False
+ reason = "'manifestfile' is missing"
+ elif action == "save":
+ # Save current products manifest to file
+ try:
+ success, reason = self._save_products(request["outfile"],
+ request["clobber"])
+ except KeyError:
+ success = False
+ reason = "'outfile' or 'clobber' is missing"
+ elif action == "reset":
+ # Reset existing products manifest
+ success = self._reset_products()
+ elif action == "convert":
+ # Convert the product from HEALPix map to HPX image
+ raise NotImplementedError("TODO")
+ else:
+ # ERROR: bad action
+ success = False
+ reason = "Bad request action: {0}".format(action)
+ #
+ if success:
+ response = {"action": action, "success": success}
+ logger.debug("Response: {0}".format(response))
+ self.set_header("Content-Type", "application/json; charset=UTF-8")
+ self.write(json_encode(response))
+ else:
+ logger.warning("Request failed: {0}".format(reason))
+ self.send_error(400, reason=reason)
+
+ def _reset_products(self):
+ """Reset the existing products manifest."""
+ self.products.reset()
+ return True
+
+ def _load_products(self, manifestfile):
+ """
+ Load the products manifest from file.
+
+ Parameters
+ ----------
+ manifestfile: str
+ The path to the products manifest file, which must be
+ an *absolute path*.
+
+ Returns
+ -------
+ success : bool
+ ``True`` if the operation succeeded, otherwise, ``False``.
+ error : str
+ If failed, this ``error`` saves the details, otherwise, ``None``.
+ """
+ success = False
+ error = None
+ if os.path.isabs(os.path.expanduser(manifestfile)):
+ self.products.load(manifestfile)
+ success = True
+ else:
+ error = "Not an absolute path"
+ return (success, error)
+
+ def _save_products(self, outfile, clobber=False):
+ """
+ Save current products manifest to file.
+
+ Parameters
+ ----------
+ outfile: str
+ The filepath to the output manifest file, which must be
+ an *absolute path*.
+ clobber : bool, optional
+ Whether overwrite the output file if already exists?
+
+ Returns
+ -------
+ success : bool
+ ``True`` if the operation succeeded, otherwise, ``False``.
+ error : str
+ If failed, this ``error`` saves the details, otherwise, ``None``.
+ """
+ success = False
+ error = None
+ try:
+ self.products.dump(outfile, clobber=clobber)
+ success = True
+ except (ValueError, OSError) as e:
+ error = str(e)
+ return (success, error)