aboutsummaryrefslogtreecommitdiffstats
path: root/fg21sim/webui/app.py
blob: 8ab19ced8d66329c7e93f9c276bae6df07009ea6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Copyright (c) 2016 Weitian LI <liweitianux@live.com>
# MIT license

"""
Web user interface (UI) of "fg21sim" based upon Tornado_ web server.

.. _Tornado: http://www.tornadoweb.org/
"""

import os
from concurrent.futures import ThreadPoolExecutor

import tornado.web
from tornado.web import url
from tornado.options import define, options

from .handlers import (IndexHandler,
                       LoginHandler,
                       ConfigsAJAXHandler,
                       ConsoleAJAXHandler,
                       ProductsAJAXHandler,
                       ProductsDownloadHandler,
                       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
define("max_workers", default=1, type=int,
       help="Maximum number of threads to execute the submitted tasks")


class Application(tornado.web.Application):
    """
    Application of the "fg21sim" Web UI.

    Attributes
    ----------
    configmanager : `~fg21sim.configs.ConfigManager`
        A ``ConfigManager`` instance, which saves the current configurations
        status.  The configuration operations (e.g., "set", "get", "load")
        are performed on this instance, which is also passed to the
        foregrounds simulation programs.
    websockets : set of `~tornado.websocket.WebSocketHandler`
        Current active WebSockets opened by clients.
        When a new WebSocket connection established, it is added to this
        list, which is also removed from this list when the connection lost.
    executor : `~concurrent.futures.ThreadPoolExecutor`
        An executor that uses a pool of threads to execute the submitted
        tasks asynchronously.
    task_status : dict
        Whether the task is running and/or finished?
        1. running=False, finished=False: not started
        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):
        self.configmanager = ConfigManager()
        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"/products/download/(.*)", ProductsDownloadHandler),
            url(r"/ws", WSHandler),
        ]
        if options.debug:
            from .handlers.base import BaseRequestHandler
            handlers.append(url(r"/debug", BaseRequestHandler, name="debug"))
        # Application settings
        settings = {
            # The static files will be served from the default "/static/" URI.
            # Recommend to use `{{ static_url(filepath) }}` in the templates.
            "static_path": os.path.join(os.path.dirname(__file__),
                                        "static"),
            "template_path": os.path.join(os.path.dirname(__file__),
                                          "templates"),
            # URL to be redirected to if the user is not logged in
            "login_url": r"/login",
            # Secret key used to sign the cookies
            "cookie_secret": gen_cookie_secret(),
            # Enable "cross-site request forgery" (XSRF) protection
            "xsrf_cookies": True,
        }
        settings.update(kwargs)
        super().__init__(handlers, **settings)