From d1e3f6219e1325e32e94de2b0c8189647c3393cb Mon Sep 17 00:00:00 2001 From: Aaron LI Date: Tue, 22 Nov 2016 17:55:21 +0800 Subject: webui: Add "ProductsAJAXHandler" to manipulate the products manifest TODO: some actions currently not implemented yet --- fg21sim/webui/app.py | 6 ++ fg21sim/webui/handlers/__init__.py | 1 + fg21sim/webui/handlers/products.py | 173 +++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 fg21sim/webui/handlers/products.py (limited to 'fg21sim/webui') 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 +# 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) -- cgit v1.2.2