Source code for tvb.interfaces.web.run

# -*- coding: utf-8 -*-
#
#
# TheVirtualBrain-Framework Package. This package holds all Data Management, and 
# Web-UI helpful to run brain-simulations. To use it, you also need to download
# TheVirtualBrain-Scientific Package (for simulators). See content of the
# documentation-folder for more details. See also http://www.thevirtualbrain.org
#
# (c) 2012-2023, Baycrest Centre for Geriatric Care ("Baycrest") and others
#
# This program is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.  See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this
# program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#   CITATION:
# When using The Virtual Brain for scientific publications, please cite it as explained here:
# https://www.thevirtualbrain.org/tvb/zwei/neuroscience-publications
#
#

"""
Launches the web server and configure the controllers for UI.

.. moduleauthor:: Lia Domide <lia.domide@codemart.ro>
"""
import time


STARTUP_TIC = time.time()

import os
import importlib
import sys
import webbrowser
import cherrypy
from cherrypy import Tool
from cherrypy.lib.sessions import RamSession
from subprocess import Popen, PIPE
from tvb.basic.logger.builder import get_logger
from tvb.basic.profile import TvbProfile
from tvb.config.init.initializer import initialize, reset
from tvb.core.adapters.abcdisplayer import ABCDisplayer
from tvb.core.decorators import user_environment_execution
from tvb.core.services.exceptions import InvalidSettingsException
from tvb.core.services.hpc_operation_service import HPCOperationService
from tvb.core.services.backend_clients.standalone_client import StandAloneClient
from tvb.interfaces.web.controllers.base_controller import BaseController
from tvb.interfaces.web.controllers.burst.dynamic_model_controller import DynamicModelController
from tvb.interfaces.web.controllers.burst.exploration_controller import ParameterExplorationController
from tvb.interfaces.web.controllers.burst.noise_configuration_controller import NoiseConfigurationController
from tvb.interfaces.web.controllers.burst.region_model_parameters_controller import RegionsModelParametersController
from tvb.interfaces.web.controllers.common import KEY_PROJECT
from tvb.interfaces.web.controllers.flow_controller import FlowController
from tvb.interfaces.web.controllers.help.help_controller import HelpController
from tvb.interfaces.web.controllers.hpc_controller import HPCController
from tvb.interfaces.web.controllers.kube_controller import KubeController
from tvb.interfaces.web.controllers.project.figure_controller import FigureController
from tvb.interfaces.web.controllers.project.project_controller import ProjectController
from tvb.interfaces.web.controllers.settings_controller import SettingsController
from tvb.interfaces.web.controllers.simulator.simulator_controller import SimulatorController
from tvb.interfaces.web.controllers.spatial.base_spatio_temporal_controller import SpatioTemporalController
from tvb.interfaces.web.controllers.spatial.local_connectivity_controller import LocalConnectivityController
from tvb.interfaces.web.controllers.spatial.region_stimulus_controller import RegionStimulusController
from tvb.interfaces.web.controllers.spatial.surface_model_parameters_controller import SurfaceModelParametersController
from tvb.interfaces.web.controllers.spatial.surface_stimulus_controller import SurfaceStimulusController
from tvb.interfaces.web.controllers.burst.transfer_vector_controller import TransferVectorController
from tvb.interfaces.web.controllers.users_controller import UserController
from tvb.interfaces.web.request_handler import RequestHandler
from tvb.storage.storage_interface import StorageInterface

if __name__ == '__main__':
    if len(sys.argv) < 2:
        TvbProfile.set_profile(TvbProfile.WEB_PROFILE)
    else:
        TvbProfile.set_profile(sys.argv[1])

LOGGER = get_logger('tvb.interfaces.web.run')
CONFIG_EXISTS = not TvbProfile.is_first_run()
PARAM_RESET_DB = "reset"
LOGGER.info("TVB application will be running using encoding: " + sys.getdefaultencoding())


[docs]class CleanupSessionHandler(RamSession): def __init__(self, id=None, **kwargs): super(CleanupSessionHandler, self).__init__(id, **kwargs)
[docs] def clean_up(self): """Clean up expired sessions.""" now = self.now() for _id, (data, expiration_time) in self.cache.copy().items(): if expiration_time <= now: if KEY_PROJECT in data: selected_project = data[KEY_PROJECT] StorageInterface().set_project_inactive(selected_project) try: del self.cache[_id] except KeyError: pass try: if self.locks[_id].acquire(blocking=False): lock = self.locks.pop(_id) lock.release() except KeyError: pass # added to remove obsolete lock objects for _id in list(self.locks): locked = ( _id not in self.cache and self.locks[_id].acquire(blocking=False) ) if locked: lock = self.locks.pop(_id) lock.release()
[docs]def init_cherrypy(arguments=None): # Mount static folders from modules marked for introspection arguments = arguments or [] CONFIGUER = TvbProfile.current.web.CHERRYPY_CONFIGURATION if StorageInterface.encryption_enabled(): CONFIGUER["/"]["tools.sessions.storage_class"] = CleanupSessionHandler for module in arguments: module_inst = importlib.import_module(str(module)) module_path = os.path.dirname(os.path.abspath(module_inst.__file__)) CONFIGUER["/static_" + str(module)] = {'tools.staticdir.on': True, 'tools.staticdir.dir': '.', 'tools.staticdir.root': module_path} # Mount controllers, and specify the root URL for them. cherrypy.tree.mount(BaseController(), BaseController.build_path("/"), config=CONFIGUER) cherrypy.tree.mount(UserController(), BaseController.build_path("/user/"), config=CONFIGUER) cherrypy.tree.mount(ProjectController(), BaseController.build_path("/project/"), config=CONFIGUER) cherrypy.tree.mount(FigureController(), BaseController.build_path("/project/figure/"), config=CONFIGUER) cherrypy.tree.mount(FlowController(), BaseController.build_path("/flow/"), config=CONFIGUER) cherrypy.tree.mount(SettingsController(), BaseController.build_path("/settings/"), config=CONFIGUER) cherrypy.tree.mount(HelpController(), BaseController.build_path("/help/"), config=CONFIGUER) cherrypy.tree.mount(SimulatorController(), BaseController.build_path("/burst/"), config=CONFIGUER) cherrypy.tree.mount(ParameterExplorationController(), BaseController.build_path("/burst/explore/"), config=CONFIGUER) cherrypy.tree.mount(DynamicModelController(), BaseController.build_path("/burst/dynamic/"), config=CONFIGUER) cherrypy.tree.mount(SpatioTemporalController(), BaseController.build_path("/spatial/"), config=CONFIGUER) cherrypy.tree.mount(RegionsModelParametersController(), BaseController.build_path("/burst/modelparameters/regions/"), config=CONFIGUER) cherrypy.tree.mount(SurfaceModelParametersController(), BaseController.build_path("/spatial/modelparameters/surface/"), config=CONFIGUER) cherrypy.tree.mount(RegionStimulusController(), BaseController.build_path("/spatial/stimulus/region/"), config=CONFIGUER) cherrypy.tree.mount(SurfaceStimulusController(), BaseController.build_path("/spatial/stimulus/surface/"), config=CONFIGUER) cherrypy.tree.mount(LocalConnectivityController(), BaseController.build_path("/spatial/localconnectivity/"), config=CONFIGUER) cherrypy.tree.mount(NoiseConfigurationController(), BaseController.build_path("/burst/noise/"), config=CONFIGUER) cherrypy.tree.mount(TransferVectorController(), TransferVectorController.build_path("/burst/transfer/"), config=CONFIGUER) cherrypy.tree.mount(HPCController(), BaseController.build_path("/hpc/"), config=CONFIGUER) cherrypy.tree.mount(KubeController(), BaseController.build_path("/kube/"), config=CONFIGUER) cherrypy.config.update(CONFIGUER) # ----------------- Register additional request handlers ----------------- # This tool checks for MAX upload size cherrypy.tools.upload = Tool('on_start_resource', RequestHandler.check_upload_size) # This tools clean up files on disk (mainly after export) cherrypy.tools.cleanup = Tool('on_end_request', RequestHandler.clean_files_on_disk) # ----------------- End register additional request handlers ---------------- # Register housekeeping job if TvbProfile.current.hpc.IS_HPC_RUN and TvbProfile.current.hpc.CAN_RUN_HPC: cherrypy.engine.housekeeper = cherrypy.process.plugins.BackgroundTask( TvbProfile.current.hpc.BACKGROUND_JOB_INTERVAL, HPCOperationService.check_operations_job) cherrypy.engine.housekeeper.start() if not TvbProfile.current.web.OPENSHIFT_DEPLOY: operations_job = cherrypy.process.plugins.BackgroundTask( TvbProfile.current.OPERATIONS_BACKGROUND_JOB_INTERVAL, StandAloneClient.process_queued_operations, bus=cherrypy.engine) operations_job.start() # HTTP Server is fired now # cherrypy.engine.start()
[docs]def expose_rest_api(): if not TvbProfile.current.KEYCLOAK_CONFIG: LOGGER.info("REST server will not start because KEYCLOAK CONFIG path is not set.") return if not os.path.exists(TvbProfile.current.KEYCLOAK_CONFIG): LOGGER.warning("Cannot start REST server because the KEYCLOAK CONFIG file {} does not exist.".format( TvbProfile.current.KEYCLOAK_CONFIG)) return if CONFIG_EXISTS: LOGGER.info("Starting Flask server with REST API...") run_params = [TvbProfile.current.PYTHON_INTERPRETER_PATH, '-m', 'tvb.interfaces.rest.server.run', TvbProfile.CURRENT_PROFILE_NAME] flask_process = Popen(run_params, stderr=PIPE) stdout, stderr = flask_process.communicate() if flask_process.returncode != 0: LOGGER.warning("Failed to start the Flask server with REST API. Stderr: {}".format(stderr)) else: LOGGER.info("Finished starting Flask server with REST API...")
[docs]def start_tvb(arguments, browser=True): """ Fire CherryPy server and listen on a free port """ if PARAM_RESET_DB in arguments: # When specified, clean everything in DB reset() arguments.remove(PARAM_RESET_DB) if not os.path.exists(TvbProfile.current.TVB_STORAGE): try: os.makedirs(TvbProfile.current.TVB_STORAGE) except Exception: sys.exit("You do not have enough rights to use TVB storage folder:" + str(TvbProfile.current.TVB_STORAGE)) try: initialize(arguments) except InvalidSettingsException as excep: LOGGER.exception(excep) sys.exit() # Mark that the interface is Web ABCDisplayer.VISUALIZERS_ROOT = TvbProfile.current.web.VISUALIZERS_ROOT init_cherrypy(arguments) if StorageInterface.encryption_enabled() and StorageInterface.app_encryption_handler(): storage_interface = StorageInterface() storage_interface.start() storage_interface.startup_cleanup() # Fire a browser page at the end. if browser: run_browser() expose_rest_api() # Launch CherryPy loop forever. LOGGER.info("Finished starting TVB version %s in %.3f s", TvbProfile.current.version.CURRENT_VERSION, time.time() - STARTUP_TIC) cherrypy.engine.block() cherrypy.log.error_log
@user_environment_execution def run_browser(): try: if TvbProfile.env.is_windows(): browser_app = webbrowser.get('windows-default') elif TvbProfile.env.is_mac(): browser_app = webbrowser.get('macosx') else: browser_app = webbrowser url_to_open = TvbProfile.current.web.BASE_LOCAL_URL if not CONFIG_EXISTS: url_to_open += 'settings/settings' LOGGER.info("We will try to open in a browser: " + url_to_open) browser_app.open(url_to_open) except Exception: LOGGER.warning("Browser could not be fired! Please manually type in your " "preferred browser: %s" % TvbProfile.current.web.BASE_LOCAL_URL) if __name__ == '__main__': # Prepare parameters and fire CherryPy # Remove not-relevant parameter, 0 should point towards this "run.py" file, 1 to the profile start_tvb(sys.argv[2:])