コード例 #1
0
class SimulatorController(BurstBaseController):
    KEY_IS_LOAD_AFTER_REDIRECT = "is_load_after_redirect"
    COPY_NAME_FORMAT = "copy_of_{}"
    BRANCH_NAME_FORMAT = "{}_branch{}"
    KEY_KEEP_SAME_SIM_WIZARD = "keep_same_wizard"

    def __init__(self):
        BurstBaseController.__init__(self)
        self.range_parameters = SimulatorRangeParameters()
        self.burst_service = BurstService()
        self.simulator_service = SimulatorService()
        self.cached_simulator_algorithm = self.algorithm_service.get_algorithm_by_module_and_class(
            IntrospectionRegistry.SIMULATOR_MODULE,
            IntrospectionRegistry.SIMULATOR_CLASS)
        self.context = SimulatorContext()
        self.monitors_handler = MonitorsWizardHandler()

    @expose_json
    def cancel_or_remove_burst(self, burst_id):
        """
        Cancel or Remove the burst entity given by burst_id (and all linked entities: op, DTs)
        :returns True: if the op was successfully.
        """
        burst_config = BurstService.load_burst_configuration(int(burst_id))
        op_id, is_group = burst_config.operation_info_for_burst_removal

        return self.cancel_or_remove_operation(op_id, is_group,
                                               burst_config.is_finished)

    def cancel_or_remove_operation(self,
                                   operation_id,
                                   is_group,
                                   remove_after_stop=False):
        """
        Stop the operation given by operation_id. If is_group is true stop all the
        operations from that group.
        """
        # Load before we remove, to have its data in memory here
        burst_config = BurstService.get_burst_for_operation_id(
            operation_id, is_group)
        if burst_config is not None:
            self.burst_service.mark_burst_finished(
                burst_config,
                BurstConfiguration.BURST_CANCELED,
                store_h5_file=False)
            while GROUP_BURST_PENDING.get(burst_config.id, False):
                pass
            GROUP_BURST_PENDING.pop(burst_config.id, False)
        result = OperationService.stop_operation(operation_id, is_group,
                                                 remove_after_stop)

        if remove_after_stop:
            current_burst = self.context.burst_config
            if (current_burst is not None and burst_config is not None
                    and current_burst.id == burst_config.id and
                ((current_burst.fk_simulation == operation_id and not is_group)
                 or (current_burst.fk_operation_group == operation_id
                     and is_group))):
                self.reset_simulator_configuration()
            if burst_config is not None:
                burst_config = BurstService.load_burst_configuration(
                    burst_config.id)
                if burst_config:
                    BurstService.remove_burst_configuration(burst_config.id)
        return result

    @expose_page
    @settings
    @context_selected
    def index(self):
        """Get on burst main page"""
        template_specification = dict(
            mainContent="burst/main_burst",
            title="Simulation Cockpit",
            includedResources='project/included_resources')

        if not self.context.last_loaded_fragment_url:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_CONNECTIVITY_URL)

        self.context.set_burst_config()

        _, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )
        if self.context.burst_config.start_time is not None:
            is_simulation_load = True
            self.context.add_simulator_load_to_session(True)

        template_specification['burstConfig'] = self.context.burst_config
        template_specification[
            'burst_list'] = self.burst_service.get_available_bursts(
                self.context.project.id)

        form = self.prepare_first_fragment()
        rendering_rules = SimulatorFragmentRenderingRules(
            form,
            SimulatorWizzardURLs.SET_CONNECTIVITY_URL,
            None,
            is_simulation_copy,
            is_simulation_load,
            last_form_url=self.context.last_loaded_fragment_url,
            last_request_type=cherrypy.request.method,
            is_first_fragment=True,
            is_branch=is_branch)
        template_specification.update(**rendering_rules.to_dict())

        cherrypy.response.headers[
            'Cache-Control'] = 'no-cache, no-store, must-revalidate'
        cherrypy.response.headers['Pragma'] = 'no-cache'
        cherrypy.response.headers['Expires'] = '0'

        return self.fill_default_attributes(template_specification)

    def prepare_first_fragment(self):
        self.context.set_simulator()
        simulator, _, _, is_branch = self.context.get_common_params()
        branch_conditions = self.simulator_service.compute_conn_branch_conditions(
            is_branch, simulator)
        form = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorAdapterForm(),
            project_id=self.context.project.id,
            extra_conditions=branch_conditions)

        self.simulator_service.validate_first_fragment(form,
                                                       self.context.project.id,
                                                       ConnectivityIndex)
        form.fill_from_trait(self.context.simulator)
        return form

    @expose_json
    def set_fragment_url(self, **data):
        try:
            self.context.add_last_loaded_form_url_to_session(data['url'])
        except KeyError:
            self.logger.error(
                "Cannot set last loaded url to session because the required data was not found."
            )

    @expose_fragment('simulator_fragment')
    def set_connectivity(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, _ = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_COUPLING_PARAMS_URL)
            form = SimulatorAdapterForm()
            form.fill_from_post(data)
            self.simulator_service.reset_at_connectivity_change(
                is_simulation_copy, form, session_stored_simulator)
            form.fill_trait(session_stored_simulator)

        next_form = self.algorithm_service.prepare_adapter_form(
            form_instance=get_form_for_coupling(
                type(session_stored_simulator.coupling))())
        self.range_parameters.coupling_parameters = next_form.get_range_parameters(
        )
        next_form.fill_from_trait(session_stored_simulator.coupling)

        rendering_rules = SimulatorFragmentRenderingRules(
            next_form, SimulatorWizzardURLs.SET_COUPLING_PARAMS_URL,
            SimulatorWizzardURLs.SET_CONNECTIVITY_URL, is_simulation_copy,
            is_simulation_load, self.context.last_loaded_fragment_url,
            cherrypy.request.method)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_coupling_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_SURFACE_URL)
            form = get_form_for_coupling(
                type(session_stored_simulator.coupling))()
            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator.coupling)

        surface_fragment = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorSurfaceFragment(),
            project_id=self.context.project.id)
        surface_fragment.fill_from_trait(session_stored_simulator.surface)

        rendering_rules = SimulatorFragmentRenderingRules(
            surface_fragment,
            SimulatorWizzardURLs.SET_SURFACE_URL,
            SimulatorWizzardURLs.SET_COUPLING_PARAMS_URL,
            is_simulation_copy,
            is_simulation_load,
            self.context.last_loaded_fragment_url,
            cherrypy.request.method,
            is_branch=is_branch)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_surface(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )
        rendering_rules = SimulatorFragmentRenderingRules(
            previous_form_action_url=SimulatorWizzardURLs.SET_SURFACE_URL,
            is_simulation_copy=is_simulation_copy,
            is_simulation_readonly_load=is_simulation_load,
            last_form_url=self.context.last_loaded_fragment_url,
            last_request_type=cherrypy.request.method,
            is_branch=is_branch)

        if cherrypy.request.method == POST_REQUEST:
            form = SimulatorSurfaceFragment()
            form.fill_from_post(data)
            self.simulator_service.reset_at_surface_change(
                is_simulation_copy, form, session_stored_simulator)
            form.fill_trait(session_stored_simulator)

            if session_stored_simulator.surface:
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_CORTEX_URL)
            else:
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_STIMULUS_URL)

        return SimulatorSurfaceFragment.prepare_next_fragment_after_surface(
            session_stored_simulator, rendering_rules, self.context.project.id,
            SimulatorWizzardURLs.SET_CORTEX_URL,
            SimulatorWizzardURLs.SET_STIMULUS_URL)

    @expose_fragment('simulator_fragment')
    def set_cortex(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, _ = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_STIMULUS_URL)
            rm_fragment = SimulatorRMFragment()
            rm_fragment.fill_from_post(data)
            rm_fragment.fill_trait(session_stored_simulator.surface)

        rendering_rules = SimulatorFragmentRenderingRules(
            None, None, SimulatorWizzardURLs.SET_CORTEX_URL,
            is_simulation_copy, is_simulation_load,
            self.context.last_loaded_fragment_url, cherrypy.request.method)

        return SimulatorStimulusFragment.prepare_stimulus_fragment(
            session_stored_simulator, rendering_rules, True,
            SimulatorWizzardURLs.SET_STIMULUS_URL, self.context.project.id)

    @expose_fragment('simulator_fragment')
    def set_stimulus(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_MODEL_URL)
            stimuli_fragment = SimulatorStimulusFragment(
                session_stored_simulator.is_surface_simulation)
            stimuli_fragment.fill_from_post(data)
            stimuli_fragment.fill_trait(session_stored_simulator)

        model_fragment = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorModelFragment())
        model_fragment.fill_from_trait(session_stored_simulator)

        rendering_rules = SimulatorFragmentRenderingRules(
            model_fragment,
            SimulatorWizzardURLs.SET_MODEL_URL,
            SimulatorWizzardURLs.SET_STIMULUS_URL,
            is_simulation_copy,
            is_simulation_load,
            self.context.last_loaded_fragment_url,
            cherrypy.request.method,
            is_model_fragment=True,
            is_surface_simulation=session_stored_simulator.
            is_surface_simulation,
            is_branch=is_branch)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_model(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            set_next_wizard = True
            if SimulatorController.KEY_KEEP_SAME_SIM_WIZARD in data:
                set_next_wizard = False
            if set_next_wizard:
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_MODEL_PARAMS_URL)
            form = SimulatorModelFragment()
            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator)

        form = self.algorithm_service.prepare_adapter_form(
            form_instance=get_form_for_model(
                type(session_stored_simulator.model))(is_branch))
        self.range_parameters.model_parameters = form.get_range_parameters()
        form.fill_from_trait(session_stored_simulator.model)

        rendering_rules = SimulatorFragmentRenderingRules(
            form, SimulatorWizzardURLs.SET_MODEL_PARAMS_URL,
            SimulatorWizzardURLs.SET_MODEL_URL, is_simulation_copy,
            is_simulation_load, self.context.last_loaded_fragment_url,
            cherrypy.request.method)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_model_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_INTEGRATOR_URL)
            form = get_form_for_model(type(
                session_stored_simulator.model))(is_branch)

            if is_branch:
                data['variables_of_interest'] = list(
                    session_stored_simulator.model.variables_of_interest)

            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator.model)

        integrator_fragment = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorIntegratorFragment())
        integrator_fragment.integrator.display_subform = False
        integrator_fragment.fill_from_trait(session_stored_simulator)

        rendering_rules = SimulatorFragmentRenderingRules(
            integrator_fragment,
            SimulatorWizzardURLs.SET_INTEGRATOR_URL,
            SimulatorWizzardURLs.SET_MODEL_PARAMS_URL,
            is_simulation_copy,
            is_simulation_load,
            self.context.last_loaded_fragment_url,
            cherrypy.request.method,
            is_branch=is_branch)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_integrator(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_INTEGRATOR_PARAMS_URL)
            fragment = SimulatorIntegratorFragment()
            fragment.fill_from_post(data)
            fragment.fill_trait(session_stored_simulator)

        form = self.algorithm_service.prepare_adapter_form(
            form_instance=get_form_for_integrator(
                type(session_stored_simulator.integrator))(is_branch))

        if hasattr(form, 'noise'):
            form.noise.display_subform = False

        form.fill_from_trait(session_stored_simulator.integrator)

        rendering_rules = SimulatorFragmentRenderingRules(
            form, SimulatorWizzardURLs.SET_INTEGRATOR_PARAMS_URL,
            SimulatorWizzardURLs.SET_INTEGRATOR_URL, is_simulation_copy,
            is_simulation_load, self.context.last_loaded_fragment_url,
            cherrypy.request.method)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_integrator_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            form = get_form_for_integrator(
                type(session_stored_simulator.integrator))(is_branch)

            if is_branch:
                data['dt'] = str(session_stored_simulator.integrator.dt)

            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator.integrator)

            if isinstance(session_stored_simulator.integrator,
                          IntegratorStochasticViewModel):
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_NOISE_PARAMS_URL)
            else:
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_MONITORS_URL)

        rendering_rules = SimulatorFragmentRenderingRules(
            None,
            None,
            SimulatorWizzardURLs.SET_INTEGRATOR_PARAMS_URL,
            is_simulation_copy,
            is_simulation_load,
            self.context.last_loaded_fragment_url,
            cherrypy.request.method,
            is_noise_fragment=False)

        if not isinstance(session_stored_simulator.integrator,
                          IntegratorStochasticViewModel):
            return self.monitors_handler.prepare_monitor_fragment(
                session_stored_simulator, rendering_rules,
                SimulatorWizzardURLs.SET_MONITORS_URL)

        integrator_noise_fragment = get_form_for_noise(
            type(session_stored_simulator.integrator.noise))()
        if hasattr(integrator_noise_fragment, 'equation'):
            integrator_noise_fragment.equation.display_subform = False

        self.range_parameters.integrator_noise_parameters = integrator_noise_fragment.get_range_parameters(
        )
        integrator_noise_fragment.fill_from_trait(
            session_stored_simulator.integrator.noise)

        rendering_rules.form = integrator_noise_fragment
        rendering_rules.form_action_url = SimulatorWizzardURLs.SET_NOISE_PARAMS_URL
        rendering_rules.is_noise_fragment = True
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_noise_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            form = get_form_for_noise(
                type(session_stored_simulator.integrator.noise))()
            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator.integrator.noise)
            if isinstance(session_stored_simulator.integrator.noise,
                          AdditiveNoiseViewModel):
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_MONITORS_URL)
            else:
                self.context.add_last_loaded_form_url_to_session(
                    SimulatorWizzardURLs.SET_NOISE_EQUATION_PARAMS_URL)

        rendering_rules = SimulatorFragmentRenderingRules(
            None, None, SimulatorWizzardURLs.SET_NOISE_PARAMS_URL,
            is_simulation_copy, is_simulation_load,
            self.context.last_loaded_fragment_url, cherrypy.request.method)

        return self.monitors_handler.prepare_next_fragment_after_noise(
            session_stored_simulator, is_branch, rendering_rules,
            SimulatorWizzardURLs.SET_MONITORS_URL,
            SimulatorWizzardURLs.SET_NOISE_EQUATION_PARAMS_URL)

    @expose_fragment('simulator_fragment')
    def set_noise_equation_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, _ = self.context.get_common_params(
        )

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_MONITORS_URL)
            form = get_form_for_equation(
                type(session_stored_simulator.integrator.noise.b))()
            form.fill_from_post(data)
            form.fill_trait(session_stored_simulator.integrator.noise.b)

        rendering_rules = SimulatorFragmentRenderingRules(
            None, None, SimulatorWizzardURLs.SET_NOISE_EQUATION_PARAMS_URL,
            is_simulation_copy, is_simulation_load,
            self.context.last_loaded_fragment_url, cherrypy.request.method)

        return self.monitors_handler.prepare_monitor_fragment(
            session_stored_simulator, rendering_rules,
            SimulatorWizzardURLs.SET_MONITORS_URL)

    @staticmethod
    def build_monitor_url(fragment_url, monitor):
        url_regex = '{}/{}'
        url = url_regex.format(fragment_url, monitor)
        return url

    def get_first_monitor_fragment_url(self, simulator, monitors_url):
        first_monitor = simulator.first_monitor
        if first_monitor is not None:
            monitor_vm_name = type(first_monitor).__name__
            return self.build_monitor_url(monitors_url, monitor_vm_name)
        return SimulatorWizzardURLs.SETUP_PSE_URL

    @expose_fragment('simulator_fragment')
    def set_monitors(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, is_branch = self.context.get_common_params(
        )
        if cherrypy.request.method == POST_REQUEST:
            fragment = SimulatorMonitorFragment(
                is_surface_simulation=session_stored_simulator.
                is_surface_simulation)
            fragment.fill_from_post(data)
            self.monitors_handler.set_monitors_list_on_simulator(
                session_stored_simulator, fragment.monitors.value)

        last_loaded_fragment_url = self.get_first_monitor_fragment_url(
            session_stored_simulator,
            SimulatorWizzardURLs.SET_MONITOR_PARAMS_URL)

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                last_loaded_fragment_url)

        rendering_rules = SimulatorFragmentRenderingRules(
            is_simulation_copy=is_simulation_copy,
            is_simulation_readonly_load=is_simulation_load,
            last_request_type=cherrypy.request.method,
            form_action_url=last_loaded_fragment_url,
            previous_form_action_url=SimulatorWizzardURLs.SET_MONITORS_URL)
        return self.monitors_handler.get_fragment_after_monitors(
            session_stored_simulator, self.context.burst_config,
            self.context.project.id, is_branch, rendering_rules,
            SimulatorWizzardURLs.SETUP_PSE_URL)

    def get_url_after_monitors(self, current_monitor, monitor_name,
                               next_monitor):
        if isinstance(current_monitor, BoldViewModel):
            return self.build_monitor_url(
                SimulatorWizzardURLs.SET_MONITOR_EQUATION_URL, monitor_name)
        if next_monitor is not None:
            return self.build_monitor_url(
                SimulatorWizzardURLs.SET_MONITOR_PARAMS_URL,
                type(next_monitor).__name__)
        return SimulatorWizzardURLs.SETUP_PSE_URL

    @staticmethod
    def get_url_for_final_fragment(burst_config):
        if burst_config.is_pse_burst():
            return SimulatorWizzardURLs.LAUNCH_PSE_URL
        return SimulatorWizzardURLs.SETUP_PSE_URL

    def get_urls_for_next_monitor_fragment(self, next_monitor,
                                           current_monitor):
        form_action_url = self.build_monitor_url(
            SimulatorWizzardURLs.SET_MONITOR_PARAMS_URL,
            type(next_monitor).__name__)
        if_bold_url = self.build_monitor_url(
            SimulatorWizzardURLs.SET_MONITOR_EQUATION_URL,
            type(current_monitor).__name__)
        return form_action_url, if_bold_url

    @expose_fragment('simulator_fragment')
    def set_monitor_params(self, current_monitor_name, **data):
        session_stored_simulator, is_simulation_copy, is_simulator_load, is_branch = self.context.get_common_params(
        )

        current_monitor, next_monitor = self.monitors_handler.get_current_and_next_monitor_form(
            current_monitor_name, session_stored_simulator)

        if cherrypy.request.method == POST_REQUEST:
            form = get_form_for_monitor(type(current_monitor))(
                session_stored_simulator, is_branch)

            if is_branch:
                data['period'] = str(current_monitor.period)
                data['variables_of_interest'] = [
                    session_stored_simulator.model.variables_of_interest[i]
                    for i in current_monitor.variables_of_interest
                ]

            form.fill_from_post(data)
            form.fill_trait(current_monitor)

            last_loaded_form_url = self.get_url_after_monitors(
                current_monitor, current_monitor_name, next_monitor)
            self.context.add_last_loaded_form_url_to_session(
                last_loaded_form_url)

        previous_form_action_url = self.build_monitor_url(
            SimulatorWizzardURLs.SET_MONITOR_PARAMS_URL, current_monitor_name)
        rendering_rules = SimulatorFragmentRenderingRules(
            is_simulation_copy=is_simulation_copy,
            is_simulation_readonly_load=is_simulator_load,
            last_request_type=cherrypy.request.method,
            last_form_url=self.context.last_loaded_fragment_url,
            previous_form_action_url=previous_form_action_url)

        form_action_url, if_bold_url = self.get_urls_for_next_monitor_fragment(
            next_monitor, current_monitor)
        self.monitors_handler.update_monitor(current_monitor)
        return self.monitors_handler.handle_next_fragment_for_monitors(
            self.context, rendering_rules, current_monitor, next_monitor,
            False, form_action_url, if_bold_url)

    def get_url_after_monitor_equation(self, next_monitor):
        if next_monitor is None:
            return SimulatorWizzardURLs.SETUP_PSE_URL

        last_loaded_fragment_url = self.build_monitor_url(
            SimulatorWizzardURLs.SET_MONITOR_PARAMS_URL,
            type(next_monitor).__name__)
        return last_loaded_fragment_url

    @expose_fragment('simulator_fragment')
    def set_monitor_equation(self, current_monitor_name, **data):
        session_stored_simulator, is_simulation_copy, is_simulator_load, is_branch = self.context.get_common_params(
        )
        current_monitor, next_monitor = self.monitors_handler.get_current_and_next_monitor_form(
            current_monitor_name, session_stored_simulator)

        if cherrypy.request.method == POST_REQUEST:
            form = get_form_for_equation(type(current_monitor.hrf_kernel))()
            form.fill_from_post(data)
            form.fill_trait(current_monitor.hrf_kernel)

            last_loaded_fragment_url = self.get_url_after_monitor_equation(
                next_monitor)
            self.context.add_last_loaded_form_url_to_session(
                last_loaded_fragment_url)

        previous_form_action_url = self.build_monitor_url(
            SimulatorWizzardURLs.SET_MONITOR_EQUATION_URL,
            current_monitor_name)
        rendering_rules = SimulatorFragmentRenderingRules(
            None, None, previous_form_action_url, is_simulation_copy,
            is_simulator_load, self.context.last_loaded_fragment_url,
            cherrypy.request)

        form_action_url, if_bold_url = self.get_urls_for_next_monitor_fragment(
            next_monitor, current_monitor)
        return self.monitors_handler.handle_next_fragment_for_monitors(
            self.context, rendering_rules, current_monitor, next_monitor, True,
            form_action_url, if_bold_url)

    @expose_fragment('simulator_fragment')
    def setup_pse(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulator_load, _ = self.context.get_common_params(
        )
        burst_config = self.context.burst_config
        all_range_parameters = self.range_parameters.get_all_range_parameters()
        next_form = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorPSEConfigurationFragment(
                self.range_parameters.get_all_range_parameters()))

        if cherrypy.request.method == POST_REQUEST:
            session_stored_simulator.simulation_length = float(
                data['simulation_length'])
            burst_config.name = data['input_simulation_name_id']
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.SET_PSE_PARAMS_URL)

        param1, param2 = self.burst_service.handle_range_params_at_loading(
            burst_config, all_range_parameters)
        if param1:
            param_dict = {'pse_param1': param1.name}
            if param2 is not None:
                param_dict['pse_param2'] = param2.name
            next_form.fill_from_post(param_dict)

        rendering_rules = SimulatorFragmentRenderingRules(
            next_form, SimulatorWizzardURLs.SET_PSE_PARAMS_URL,
            SimulatorWizzardURLs.SETUP_PSE_URL, is_simulation_copy,
            is_simulator_load, self.context.last_loaded_fragment_url,
            cherrypy.request.method)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def set_pse_params(self, **data):
        session_stored_simulator, is_simulation_copy, is_simulation_load, _ = self.context.get_common_params(
        )
        burst_config = self.context.burst_config
        form = SimulatorPSEConfigurationFragment(
            self.range_parameters.get_all_range_parameters())

        if cherrypy.request.method == POST_REQUEST:
            self.context.add_last_loaded_form_url_to_session(
                SimulatorWizzardURLs.LAUNCH_PSE_URL)
            form.fill_from_post(data)

            param1 = form.pse_param1.value
            burst_config.range1 = param1.to_json()
            param2 = None
            if form.pse_param2.value:
                param2 = form.pse_param2.value
                burst_config.range2 = param2.to_json()
        else:
            all_range_parameters = self.range_parameters.get_all_range_parameters(
            )
            param1, param2 = self.burst_service.handle_range_params_at_loading(
                burst_config, all_range_parameters)
        next_form = self.algorithm_service.prepare_adapter_form(
            form_instance=SimulatorPSERangeFragment(param1, param2))

        rendering_rules = SimulatorFragmentRenderingRules(
            next_form,
            SimulatorWizzardURLs.LAUNCH_PSE_URL,
            SimulatorWizzardURLs.SET_PSE_PARAMS_URL,
            is_simulation_copy,
            is_simulation_load,
            last_form_url=self.context.last_loaded_fragment_url,
            is_launch_pse_fragment=True)
        return rendering_rules.to_dict()

    @expose_json
    def launch_pse(self, **data):
        session_stored_simulator = self.context.simulator
        all_range_parameters = self.range_parameters.get_all_range_parameters()
        range_param1, range_param2 = SimulatorPSERangeFragment.fill_from_post(
            all_range_parameters, **data)

        burst_config = self.context.burst_config
        burst_config.start_time = datetime.now()
        burst_config.range1 = range_param1.to_json()
        if range_param2:
            burst_config.range2 = range_param2.to_json()
        burst_config = self.burst_service.prepare_burst_for_pse(burst_config)
        session_stored_simulator.operation_group_gid = uuid.UUID(
            burst_config.operation_group.gid)
        session_stored_simulator.ranges = json.dumps(burst_config.ranges)

        try:
            thread = threading.Thread(
                target=self.simulator_service.async_launch_and_prepare_pse,
                kwargs={
                    'burst_config': burst_config,
                    'user': self.context.logged_user,
                    'project': self.context.project,
                    'simulator_algo': self.cached_simulator_algorithm,
                    'range_param1': range_param1,
                    'range_param2': range_param2,
                    'session_stored_simulator': session_stored_simulator
                })
            thread.start()
            return {'id': burst_config.id}
        except BurstServiceException as e:
            self.logger.exception("Could not launch burst!")
            return {'error': e.message}

    @expose_json
    def launch_simulation(self, launch_mode, **data):
        current_form = SimulatorFinalFragment()
        burst_config = self.context.burst_config
        burst_config.range1 = None
        burst_config.range2 = None

        try:
            current_form.fill_from_post(data)
        except Exception as exc:
            self.logger.exception(exc)
            return {'error': str(exc)}

        burst_name = current_form.simulation_name.value
        session_stored_simulator = self.context.simulator
        session_stored_simulator.simulation_length = current_form.simulation_length.value

        if burst_name != 'none_undefined':
            burst_config.name = burst_name

        if launch_mode == self.burst_service.LAUNCH_BRANCH:
            simulation_state_index = self.simulator_service.get_simulation_state_index(
                burst_config, SimulationHistoryIndex)
            session_stored_simulator.history_gid = simulation_state_index[
                0].gid

        burst_config.start_time = datetime.now()
        session_burst_config = self.burst_service.store_burst(burst_config)

        try:
            thread = threading.Thread(target=self.simulator_service.
                                      async_launch_and_prepare_simulation,
                                      kwargs={
                                          'burst_config': session_burst_config,
                                          'user': self.context.logged_user,
                                          'project': self.context.project,
                                          'simulator_algo':
                                          self.cached_simulator_algorithm,
                                          'simulator': session_stored_simulator
                                      })
            thread.start()
            return {'id': session_burst_config.id}
        except BurstServiceException as e:
            self.logger.exception('Could not launch burst!')
            return {'error': e.message}

    @expose_fragment('burst/burst_history')
    def load_burst_history(self, initBurst=None):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        bursts = self.burst_service.get_available_bursts(
            self.context.project.id)
        self.burst_service.populate_burst_disk_usage(bursts)
        fromInit = False
        if initBurst is not None:
            fromInit = True
        return {
            'burst_list': bursts,
            'fromInit': fromInit,
            'selectedBurst': self.context.burst_config.id,
            'first_fragment_url':
            SimulatorFragmentRenderingRules.FIRST_FORM_URL
        }

    @cherrypy.expose
    def get_last_fragment_url(self, burst_config_id):
        burst_config = self.burst_service.load_burst_configuration(
            burst_config_id)
        return self.get_url_for_final_fragment(burst_config)

    @expose_fragment('simulator_fragment')
    def load_burst_read_only(self, burst_config_id):
        try:
            burst_config = self.burst_service.load_burst_configuration(
                burst_config_id)
            storage_path = StorageInterface().get_project_folder(
                self.context.project.name, str(burst_config.fk_simulation))
            simulator = h5.load_view_model(burst_config.simulator_gid,
                                           storage_path)
            last_loaded_form_url = self.get_url_for_final_fragment(
                burst_config)
            self.context.init_session_at_burst_loading(burst_config, simulator,
                                                       last_loaded_form_url)

            form = self.prepare_first_fragment()
            self.monitors_handler.build_list_of_monitors_from_view_models(
                self.context.simulator)
            rendering_rules = SimulatorFragmentRenderingRules(
                form,
                SimulatorWizzardURLs.SET_CONNECTIVITY_URL,
                is_simulation_readonly_load=True,
                is_first_fragment=True)
            return rendering_rules.to_dict()
        except Exception:
            # Most probably Burst was removed. Delete it from session, so that client
            # has a good chance to get a good response on refresh
            self.logger.exception("Error loading burst")
            self.context.remove_burst_config_from_session()
            raise

    def _prepare_first_fragment_for_burst_copy(self, burst_config_id,
                                               burst_name_format):
        simulator, burst_config_copy = self.burst_service.prepare_data_for_burst_copy(
            burst_config_id, burst_name_format, self.context.project)
        self.monitors_handler.build_list_of_monitors_from_view_models(
            simulator)

        last_loaded_form_url = self.get_url_for_final_fragment(
            burst_config_copy)
        self.context.init_session_at_copy_preparation(burst_config_copy,
                                                      simulator,
                                                      last_loaded_form_url)
        return self.prepare_first_fragment()

    @expose_fragment('simulator_fragment')
    def copy_simulator_configuration(self, burst_config_id):
        self.context.add_branch_and_copy_to_session(False, True)
        form = self._prepare_first_fragment_for_burst_copy(
            burst_config_id, self.COPY_NAME_FORMAT)
        rendering_rules = SimulatorFragmentRenderingRules(
            form,
            SimulatorWizzardURLs.SET_CONNECTIVITY_URL,
            is_simulation_copy=True,
            is_simulation_readonly_load=True,
            is_first_fragment=True)
        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def branch_simulator_configuration(self, burst_config_id):
        self.context.add_branch_and_copy_to_session(True, False)
        form = self._prepare_first_fragment_for_burst_copy(
            burst_config_id, self.BRANCH_NAME_FORMAT)
        rendering_rules = SimulatorFragmentRenderingRules(
            form,
            SimulatorWizzardURLs.SET_CONNECTIVITY_URL,
            is_simulation_copy=True,
            is_simulation_readonly_load=True,
            is_first_fragment=True)

        return rendering_rules.to_dict()

    @expose_fragment('simulator_fragment')
    def reset_simulator_configuration(self):
        burst_config = BurstConfiguration(self.context.project.id)
        self.context.init_session_at_sim_reset(
            burst_config, SimulatorWizzardURLs.SET_CONNECTIVITY_URL)
        self.monitors_handler.clear_next_monitors_dict()

        form = self.prepare_first_fragment()
        rendering_rules = SimulatorFragmentRenderingRules(
            form,
            SimulatorWizzardURLs.SET_CONNECTIVITY_URL,
            is_first_fragment=True)
        return rendering_rules.to_dict()

    @expose_json
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        validation_result = SimulatorFinalFragment.is_burst_name_ok(burst_name)
        if validation_result is True:
            self.burst_service.rename_burst(burst_id, burst_name)
            return {'success': "Simulation successfully renamed!"}
        else:
            self.logger.exception(validation_result)
            return {'error': validation_result}

    @expose_json
    def get_history_status(self, **data):
        """
        For each burst id received, get the status and return it.
        """
        return self.burst_service.update_history_status(
            json.loads(data['burst_ids']))

    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def export(self, burst_id):
        export_manager = ExportManager()
        export_zip = export_manager.export_simulator_configuration(burst_id)

        result_name = "tvb_simulation_" + str(burst_id) + ".zip"
        return serve_file(export_zip, "application/x-download", "attachment",
                          result_name)

    @expose_fragment("overlay")
    def get_upload_overlay(self):
        template_specification = self.fill_overlay_attributes(
            None, "Upload", "Simulation ZIP", "burst/upload_burst_overlay",
            "dialog-upload")
        template_specification[
            'first_fragment_url'] = SimulatorWizzardURLs.SET_CONNECTIVITY_URL
        return self.fill_default_attributes(template_specification)

    @cherrypy.expose
    @handle_error(redirect=True)
    @check_user
    @settings
    def load_simulator_configuration_from_zip(self, **data):
        """Upload Simulator from previously exported ZIP file"""
        self.logger.debug("Uploading ..." + str(data))
        last_loaded_form_url = SimulatorWizzardURLs.SETUP_PSE_URL

        try:
            upload_param = "uploadedfile"
            if upload_param in data and data[upload_param]:
                simulator, burst_config, sim_folder = self.burst_service.load_simulation_from_zip(
                    data[upload_param], self.context.project)

                dts_folder = os.path.join(
                    sim_folder, StorageInterface.EXPORTED_SIMULATION_DTS_DIR)
                ImportService().import_project_operations(
                    self.context.project, dts_folder, False, None)

                self.monitors_handler.build_list_of_monitors_from_view_models(
                    simulator)
                if burst_config.is_pse_burst():
                    last_loaded_form_url = SimulatorWizzardURLs.LAUNCH_PSE_URL
                self.context.init_session_at_sim_config_from_zip(
                    burst_config, simulator, last_loaded_form_url)
        except IOError as ioexcep:
            self.logger.exception(ioexcep)
            self.context.set_warning_message(
                "This ZIP does not contain a complete simulator configuration")
        except ServicesBaseException as excep:
            self.logger.warning(excep.message)
            self.context.set_warning_message(excep.message)

        raise cherrypy.HTTPRedirect('/burst/')
コード例 #2
0
class BurstController(BurstBaseController):
    """
    Controller class for Burst-Pages.
    """


    def __init__(self):
        BurstBaseController.__init__(self)
        self.burst_service = BurstService()
        self.workflow_service = WorkflowService()
        self.context = SelectedAdapterContext()

        ## Cache simulator Tree and Algorithm for performance issues.
        self.cached_simulator_algorithm = self.flow_service.get_algorithm_by_module_and_class(SIMULATOR_MODULE,
                                                                                              SIMULATOR_CLASS)


    @property
    @context_selected
    def cached_simulator_input_tree(self):
        """
        Cache Simulator's input tree, for performance issues.
        Anyway, without restart, the introspected tree will not be different on multiple executions.
        :returns: Simulator's Input Tree (copy from cache or just loaded)
        """
        cached_simulator_tree = common.get_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        if cached_simulator_tree is None:
            cached_simulator_tree = self.flow_service.prepare_adapter(common.get_current_project().id,
                                                                      self.cached_simulator_algorithm)
            common.add2session(common.KEY_CACHED_SIMULATOR_TREE, cached_simulator_tree)
        return copy.deepcopy(cached_simulator_tree)


    @expose_page
    @settings
    @context_selected
    def index(self):
        """Get on burst main page"""
        # todo : reuse load_burst here for consistency.
        template_specification = dict(mainContent="burst/main_burst", title="Simulation Cockpit",
                                      baseUrl=TvbProfile.current.web.BASE_URL,
                                      includedResources='project/included_resources')
        portlets_list = self.burst_service.get_available_portlets()
        session_stored_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_stored_burst is None or session_stored_burst.id is None:
            if session_stored_burst is None:
                session_stored_burst = self.burst_service.new_burst_configuration(common.get_current_project().id)
                common.add2session(common.KEY_BURST_CONFIG, session_stored_burst)

            adapter_interface = self.cached_simulator_input_tree
            if session_stored_burst is not None:
                current_data = session_stored_burst.get_all_simulator_values()[0]
                adapter_interface = InputTreeManager.fill_defaults(adapter_interface, current_data, True)
                ### Add simulator tree to session to be available in filters
                self.context.add_adapter_to_session(self.cached_simulator_algorithm, adapter_interface, current_data)
            template_specification['inputList'] = adapter_interface

        selected_portlets = session_stored_burst.update_selected_portlets()
        template_specification['burst_list'] = self.burst_service.get_available_bursts(common.get_current_project().id)
        template_specification['portletList'] = portlets_list
        template_specification['selectedPortlets'] = json.dumps(selected_portlets)
        template_specification['draw_hidden_ranges'] = True
        template_specification['burstConfig'] = session_stored_burst

        ### Prepare PSE available metrics
        ### We put here all available algorithms, because the metrics select area is a generic one, 
        ### and not loaded with every Burst Group change in history.
        algorithm = self.flow_service.get_algorithm_by_module_and_class(MEASURE_METRICS_MODULE, MEASURE_METRICS_CLASS)
        adapter_instance = ABCAdapter.build_adapter(algorithm)
        if adapter_instance is not None and hasattr(adapter_instance, 'available_algorithms'):
            template_specification['available_metrics'] = [metric_name for metric_name
                                                           in adapter_instance.available_algorithms.keys()]
        else:
            template_specification['available_metrics'] = []

        template_specification[common.KEY_PARAMETERS_CONFIG] = False
        template_specification[common.KEY_SECTION] = 'burst'
        return self.fill_default_attributes(template_specification)


    @expose_fragment('burst/burst_history')
    def load_burst_history(self):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        bursts = self.burst_service.get_available_bursts(common.get_current_project().id)
        self.burst_service.populate_burst_disk_usage(bursts)
        return {'burst_list': bursts,
                'selectedBurst': session_burst.id}


    @cherrypy.expose
    @handle_error(redirect=False)
    def get_selected_burst(self):
        """
        Return the burst that is currently stored in session.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_burst.id:
            return str(session_burst.id)
        else:
            return 'None'


    @expose_fragment('burst/portlet_configure_parameters')
    def get_portlet_configurable_interface(self, index_in_tab):
        """
        From the position given by the tab index and the index from that tab, 
        get the portlet configuration and build the configurable interface
        for that portlet.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_index = burst_config.selected_tab
        portlet_config = burst_config.tabs[tab_index].portlets[int(index_in_tab)]
        portlet_interface = self.burst_service.build_portlet_interface(portlet_config, common.get_current_project().id)

        full_portlet_input_tree = []
        for entry in portlet_interface:
            full_portlet_input_tree.extend(entry.interface)
        self.context.add_portlet_to_session(full_portlet_input_tree)

        portlet_interface = {"adapters_list": portlet_interface,
                             common.KEY_PARAMETERS_CONFIG: False,
                             common.KEY_SESSION_TREE: self.context.KEY_PORTLET_CONFIGURATION}
        return self.fill_default_attributes(portlet_interface)


    @expose_fragment('burst/portlets_preview')
    def portlet_tab_display(self, **data):
        """
        When saving a new configuration of tabs, check if any of the old 
        portlets are still present, and if that is the case use their 
        parameters configuration. 
        
        For all the new portlets add entries in the burst configuration. 
        Also remove old portlets that are no longer saved.
        """
        tab_portlets_list = json.loads(data['tab_portlets_list'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab_idx = burst_config.selected_tab
        for tab_idx in range(len(tab_portlets_list)):
            current_tab = burst_config.tabs[tab_idx]
            ### When configuration already exists, and new portlets          #####
            ### are selected, first check if any configuration was saved for #####
            ### each portlet and if that is the case, use it. If none is present #
            ### create a new one.                                              ###
            for idx_in_tab in range(len(tab_portlets_list[tab_idx])):
                portlet_id = tab_portlets_list[tab_idx][idx_in_tab][0]
                portlet_name = tab_portlets_list[tab_idx][idx_in_tab][1]
                if portlet_id >= 0:
                    saved_config = current_tab.portlets[idx_in_tab]
                    if saved_config is None or saved_config.portlet_id != portlet_id:
                        current_tab.portlets[idx_in_tab] = self.burst_service.new_portlet_configuration(portlet_id,
                                                                                    tab_idx, idx_in_tab, portlet_name)
                    else:
                        saved_config.visualizer.ui_name = portlet_name
                else:
                    current_tab.portlets[idx_in_tab] = None
            #For generating the HTML get for each id the corresponding portlet
        selected_tab_portlets = []
        saved_selected_tab = burst_config.tabs[selected_tab_idx]
        for portlet in saved_selected_tab.portlets:
            if portlet:
                portlet_id = int(portlet.portlet_id)
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_id)
                portlet_entity.name = portlet.name
                selected_tab_portlets.append(portlet_entity)

        return {'portlet_tab_list': selected_tab_portlets}


    @expose_fragment('burst/portlets_preview')
    def get_configured_portlets(self):
        """
        Return the portlets for one given tab. This is used when changing
        from tab to tab and selecting which portlets will be displayed.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        if burst_config is None:
            return {'portlet_tab_list': []}

        tab_idx = burst_config.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst_config.tabs[int(tab_idx)].portlets:
            if portlet_cfg is not None:
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_cfg.portlet_id)
                portlet_entity.name = portlet_cfg.name
                tab_portlet_list.append(portlet_entity)
        return {'portlet_tab_list': tab_portlet_list}


    @expose_json
    def change_selected_tab(self, tab_nr):
        """
        Set :param tab_nr: as the currently selected tab in the stored burst
        configuration. 
        """
        common.get_from_session(common.KEY_BURST_CONFIG).selected_tab = int(tab_nr)


    @expose_json
    def get_portlet_session_configuration(self):
        """
        Get the current configuration of portlets stored in session for this burst,
        as a json.
        """
        burst_entity = common.get_from_session(common.KEY_BURST_CONFIG)
        returned_configuration = burst_entity.update_selected_portlets()
        return returned_configuration


    @cherrypy.expose
    @handle_error(redirect=False)
    def save_parameters(self, index_in_tab, **data):
        """
        Save parameters
        
        :param tab_nr: the index of the selected tab
        :param index_in_tab: the index of the configured portlet in the selected tab
        :param data: the {"portlet_parameters": json_string} Where json_string is a Jsonified dictionary
            {"name": value}, representing the configuration of the current portlet
        
        Having these inputs, current method updated the configuration of the portlet in the
        corresponding tab position form the burst configuration in session.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_nr = burst_config.selected_tab
        old_portlet_config = burst_config.tabs[int(tab_nr)].portlets[int(index_in_tab)]
        data = json.loads(data['portlet_parameters'])

        # Replace all void entries with 'None'
        for entry in data:
            if data[entry] == '':
                data[entry] = None

        need_relaunch = self.burst_service.update_portlet_configuration(old_portlet_config, data)
        if need_relaunch:
            #### Reset Burst Configuration into an entity not persisted (id = None for all)
            common.add2session(common.KEY_BURST_CONFIG, burst_config.clone())
            return "relaunchView"
        else:
            self.workflow_service.store_workflow_step(old_portlet_config.visualizer)
            return "noRelaunch"


    @expose_json
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        validation_result = self._is_burst_name_ok(burst_name)
        if validation_result is True:
            self.burst_service.rename_burst(burst_id, burst_name)
            return {'success': "Simulation successfully renamed!"}
        else:
            return {'error': validation_result}


    @expose_json
    def launch_burst(self, launch_mode, burst_name, **data):
        """
        Do the actual burst launch, using the configuration saved in current session.
        :param launch_mode: new/branch/continue
        :param burst_name: user-given burst name. It can be empty (case in which we will fill with simulation_x)
        :param data: kwargs for simulation input parameters.
        """
        data = json.loads(data['simulator_parameters'])
        simulation_length = data['simulation_length']
        try:
            simulation_length = total_ms(simulation_length)
        except ValueError, e:
            return {'error': e.message}
        data['simulation_length']=unicode(simulation_length)
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)

        ## Validate new burst-name
        if launch_mode == LAUNCH_NEW and burst_name != 'none_undefined':
            validation_result = self._is_burst_name_ok(burst_name)
            if validation_result is True:
                burst_config.name = burst_name
            else:
                return {'error': validation_result}

        ## Fill all parameters 
        user_id = common.get_logged_user().id
        data[common.KEY_ADAPTER] = self.cached_simulator_algorithm.id
        burst_config.update_simulator_configuration(data)
        burst_config.fk_project = common.get_current_project().id

        ## Do the asynchronous launch
        try:
            burst_id, burst_name = self.burst_service.launch_burst(burst_config, 0, self.cached_simulator_algorithm.id,
                                                                   user_id, launch_mode)
            return {'id': burst_id, 'name': burst_name}
        except BurstServiceException as e:
            self.logger.exception("Could not launch burst!")
            return {'error': e.message}
コード例 #3
0
class BurstController(BurstBaseController):
    """
    Controller class for Burst-Pages.
    """
    def __init__(self):
        BurstBaseController.__init__(self)
        self.burst_service = BurstService()
        self.workflow_service = WorkflowService()
        self.context = SelectedAdapterContext()

        ## Cache simulator Tree and Algorithm for performance issues.
        self.cached_simulator_algorithm = self.flow_service.get_algorithm_by_module_and_class(
            SIMULATOR_MODULE, SIMULATOR_CLASS)

    @property
    @context_selected
    def cached_simulator_input_tree(self):
        """
        Cache Simulator's input tree, for performance issues.
        Anyway, without restart, the introspected tree will not be different on multiple executions.
        :returns: Simulator's Input Tree (copy from cache or just loaded)
        """
        cached_simulator_tree = common.get_from_session(
            common.KEY_CACHED_SIMULATOR_TREE)
        if cached_simulator_tree is None:
            cached_simulator_tree = self.flow_service.prepare_adapter(
                common.get_current_project().id,
                self.cached_simulator_algorithm)
            common.add2session(common.KEY_CACHED_SIMULATOR_TREE,
                               cached_simulator_tree)
        return copy.deepcopy(cached_simulator_tree)

    @expose_page
    @settings
    @context_selected
    def index(self):
        """Get on burst main page"""
        # todo : reuse load_burst here for consistency.
        template_specification = dict(
            mainContent="burst/main_burst",
            title="Simulation Cockpit",
            baseUrl=TvbProfile.current.web.BASE_URL,
            includedResources='project/included_resources')
        portlets_list = self.burst_service.get_available_portlets()
        session_stored_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_stored_burst is None or session_stored_burst.id is None:
            if session_stored_burst is None:
                session_stored_burst = self.burst_service.new_burst_configuration(
                    common.get_current_project().id)
                common.add2session(common.KEY_BURST_CONFIG,
                                   session_stored_burst)

            adapter_interface = self.cached_simulator_input_tree
            if session_stored_burst is not None:
                current_data = session_stored_burst.get_all_simulator_values(
                )[0]
                adapter_interface = InputTreeManager.fill_defaults(
                    adapter_interface, current_data, True)
                ### Add simulator tree to session to be available in filters
                self.context.add_adapter_to_session(
                    self.cached_simulator_algorithm, adapter_interface,
                    current_data)
            template_specification['inputList'] = adapter_interface

        selected_portlets = session_stored_burst.update_selected_portlets()
        template_specification[
            'burst_list'] = self.burst_service.get_available_bursts(
                common.get_current_project().id)
        template_specification['portletList'] = portlets_list
        template_specification['selectedPortlets'] = json.dumps(
            selected_portlets)
        template_specification['draw_hidden_ranges'] = True
        template_specification['burstConfig'] = session_stored_burst

        ### Prepare PSE available metrics
        ### We put here all available algorithms, because the metrics select area is a generic one,
        ### and not loaded with every Burst Group change in history.
        algorithm = self.flow_service.get_algorithm_by_module_and_class(
            MEASURE_METRICS_MODULE, MEASURE_METRICS_CLASS)
        adapter_instance = ABCAdapter.build_adapter(algorithm)
        if adapter_instance is not None and hasattr(adapter_instance,
                                                    'available_algorithms'):
            template_specification['available_metrics'] = [
                metric_name for metric_name in
                adapter_instance.available_algorithms.keys()
            ]
        else:
            template_specification['available_metrics'] = []

        template_specification[common.KEY_PARAMETERS_CONFIG] = False
        template_specification[common.KEY_SECTION] = 'burst'
        return self.fill_default_attributes(template_specification)

    @expose_fragment('burst/burst_history')
    def load_burst_history(self):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        bursts = self.burst_service.get_available_bursts(
            common.get_current_project().id)
        self.burst_service.populate_burst_disk_usage(bursts)
        return {'burst_list': bursts, 'selectedBurst': session_burst.id}

    @cherrypy.expose
    @handle_error(redirect=False)
    def get_selected_burst(self):
        """
        Return the burst that is currently stored in session.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_burst.id:
            return str(session_burst.id)
        else:
            return 'None'

    @expose_fragment('burst/portlet_configure_parameters')
    def get_portlet_configurable_interface(self, index_in_tab):
        """
        From the position given by the tab index and the index from that tab, 
        get the portlet configuration and build the configurable interface
        for that portlet.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_index = burst_config.selected_tab
        portlet_config = burst_config.tabs[tab_index].portlets[int(
            index_in_tab)]
        portlet_interface = self.burst_service.build_portlet_interface(
            portlet_config,
            common.get_current_project().id)

        full_portlet_input_tree = []
        for entry in portlet_interface:
            full_portlet_input_tree.extend(entry.interface)
        self.context.add_portlet_to_session(full_portlet_input_tree)

        portlet_interface = {
            "adapters_list": portlet_interface,
            common.KEY_PARAMETERS_CONFIG: False,
            common.KEY_SESSION_TREE: self.context.KEY_PORTLET_CONFIGURATION
        }
        return self.fill_default_attributes(portlet_interface)

    @expose_fragment('burst/portlets_preview')
    def portlet_tab_display(self, **data):
        """
        When saving a new configuration of tabs, check if any of the old 
        portlets are still present, and if that is the case use their 
        parameters configuration. 
        
        For all the new portlets add entries in the burst configuration. 
        Also remove old portlets that are no longer saved.
        """
        tab_portlets_list = json.loads(data['tab_portlets_list'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab_idx = burst_config.selected_tab
        for tab_idx in range(len(tab_portlets_list)):
            current_tab = burst_config.tabs[tab_idx]
            ### When configuration already exists, and new portlets          #####
            ### are selected, first check if any configuration was saved for #####
            ### each portlet and if that is the case, use it. If none is present #
            ### create a new one.                                              ###
            for idx_in_tab in range(len(tab_portlets_list[tab_idx])):
                portlet_id = tab_portlets_list[tab_idx][idx_in_tab][0]
                portlet_name = tab_portlets_list[tab_idx][idx_in_tab][1]
                if portlet_id >= 0:
                    saved_config = current_tab.portlets[idx_in_tab]
                    if saved_config is None or saved_config.portlet_id != portlet_id:
                        current_tab.portlets[
                            idx_in_tab] = self.burst_service.new_portlet_configuration(
                                portlet_id, tab_idx, idx_in_tab, portlet_name)
                    else:
                        saved_config.visualizer.ui_name = portlet_name
                else:
                    current_tab.portlets[idx_in_tab] = None
            #For generating the HTML get for each id the corresponding portlet
        selected_tab_portlets = []
        saved_selected_tab = burst_config.tabs[selected_tab_idx]
        for portlet in saved_selected_tab.portlets:
            if portlet:
                portlet_id = int(portlet.portlet_id)
                portlet_entity = self.burst_service.get_portlet_by_id(
                    portlet_id)
                portlet_entity.name = portlet.name
                selected_tab_portlets.append(portlet_entity)

        return {'portlet_tab_list': selected_tab_portlets}

    @expose_fragment('burst/portlets_preview')
    def get_configured_portlets(self):
        """
        Return the portlets for one given tab. This is used when changing
        from tab to tab and selecting which portlets will be displayed.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        if burst_config is None:
            return {'portlet_tab_list': []}

        tab_idx = burst_config.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst_config.tabs[int(tab_idx)].portlets:
            if portlet_cfg is not None:
                portlet_entity = self.burst_service.get_portlet_by_id(
                    portlet_cfg.portlet_id)
                portlet_entity.name = portlet_cfg.name
                tab_portlet_list.append(portlet_entity)
        return {'portlet_tab_list': tab_portlet_list}

    @expose_json
    def change_selected_tab(self, tab_nr):
        """
        Set :param tab_nr: as the currently selected tab in the stored burst
        configuration. 
        """
        common.get_from_session(
            common.KEY_BURST_CONFIG).selected_tab = int(tab_nr)

    @expose_json
    def get_portlet_session_configuration(self):
        """
        Get the current configuration of portlets stored in session for this burst,
        as a json.
        """
        burst_entity = common.get_from_session(common.KEY_BURST_CONFIG)
        returned_configuration = burst_entity.update_selected_portlets()
        return returned_configuration

    @cherrypy.expose
    @handle_error(redirect=False)
    def save_parameters(self, index_in_tab, **data):
        """
        Save parameters
        
        :param tab_nr: the index of the selected tab
        :param index_in_tab: the index of the configured portlet in the selected tab
        :param data: the {"portlet_parameters": json_string} Where json_string is a Jsonified dictionary
            {"name": value}, representing the configuration of the current portlet
        
        Having these inputs, current method updated the configuration of the portlet in the
        corresponding tab position form the burst configuration in session.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_nr = burst_config.selected_tab
        old_portlet_config = burst_config.tabs[int(tab_nr)].portlets[int(
            index_in_tab)]
        data = json.loads(data['portlet_parameters'])

        # Replace all void entries with 'None'
        for entry in data:
            if data[entry] == '':
                data[entry] = None

        need_relaunch = self.burst_service.update_portlet_configuration(
            old_portlet_config, data)
        if need_relaunch:
            #### Reset Burst Configuration into an entity not persisted (id = None for all)
            common.add2session(common.KEY_BURST_CONFIG, burst_config.clone())
            return "relaunchView"
        else:
            self.workflow_service.store_workflow_step(
                old_portlet_config.visualizer)
            return "noRelaunch"

    @expose_json
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        validation_result = self._is_burst_name_ok(burst_name)
        if validation_result is True:
            self.burst_service.rename_burst(burst_id, burst_name)
            return {'success': "Simulation successfully renamed!"}
        else:
            return {'error': validation_result}

    @expose_json
    def launch_burst(self, launch_mode, burst_name, **data):
        """
        Do the actual burst launch, using the configuration saved in current session.
        :param launch_mode: new/branch/continue
        :param burst_name: user-given burst name. It can be empty (case in which we will fill with simulation_x)
        :param data: kwargs for simulation input parameters.
        """
        data = json.loads(data['simulator_parameters'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)

        ## Validate new burst-name
        if launch_mode == LAUNCH_NEW and burst_name != 'none_undefined':
            validation_result = self._is_burst_name_ok(burst_name)
            if validation_result is True:
                burst_config.name = burst_name
            else:
                return {'error': validation_result}

        ## Fill all parameters
        user_id = common.get_logged_user().id
        data[common.KEY_ADAPTER] = self.cached_simulator_algorithm.id
        burst_config.update_simulator_configuration(data)
        burst_config.fk_project = common.get_current_project().id

        ## Do the asynchronous launch
        try:
            burst_id, burst_name = self.burst_service.launch_burst(
                burst_config, 0, self.cached_simulator_algorithm.id, user_id,
                launch_mode)
            return {'id': burst_id, 'name': burst_name}
        except BurstServiceException as e:
            self.logger.exception("Could not launch burst!")
            return {'error': e.message}

    @expose_json
    def load_burst(self, burst_id):
        """
        Given a burst id return its running status, weather it was a operation group and the selected tab.
        This is called when a burst is selected in the history,
        when returning from a burst config page (model param or noise)
        and when the status of running simulations is polled.
        Besides returning these values it updates the session stored burst.

        A burst configuration has 2 meanings.
        It is a staging configuration for a new burst (stored in transients in the session).
        It is the configuration used to launch a simulation and it's running status (stored in the db).
        This method has to merge the two meanings.
        If the requested burst_id is different from the one held in the session,
        then the burst config is loaded from the db, discarding any session stored config.
        If the id is the same then the session config is kept.
        """
        try:
            burst_id = int(burst_id)
            old_burst = common.get_from_session(common.KEY_BURST_CONFIG)
            burst, group_gid = self.burst_service.load_burst(burst_id)

            if old_burst and old_burst.id == burst_id:
                # This function was called to reload the current burst.
                # Merge session config into the db config. Overwrite all transient fields
                burst.simulator_configuration = old_burst.simulator_configuration
                burst.dynamic_ids = old_burst.dynamic_ids

            burst.selected_tab = old_burst.selected_tab
            common.add2session(common.KEY_BURST_CONFIG, burst)
            return {
                'status': burst.status,
                'group_gid': group_gid,
                'selected_tab': burst.selected_tab
            }
        except Exception:
            ### Most probably Burst was removed. Delete it from session, so that client
            ### has a good chance to get a good response on refresh
            self.logger.exception("Error loading burst")
            common.remove_from_session(common.KEY_BURST_CONFIG)
            raise

    @expose_json
    def get_history_status(self, **data):
        """
        For each burst id received, get the status and return it.
        """
        return self.burst_service.update_history_status(
            json.loads(data['burst_ids']))

    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def cancel_or_remove_burst(self, burst_id):
        """
        Cancel or Remove the burst entity given by burst_id.
        :returns 'reset-new': When currently selected burst was removed. JS will need to reset selection to a new entry
        :returns 'canceled': When current burst was still running and was just stopped.
        :returns 'done': When no action is required on the client.
        """
        burst_id = int(burst_id)
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        removed = self.burst_service.cancel_or_remove_burst(burst_id)
        if removed:
            if session_burst.id == burst_id:
                return "reset-new"
            return 'done'
        else:
            # Burst was stopped since it was running
            return 'canceled'

    @expose_json
    def get_selected_portlets(self):
        """
        Get the selected portlets for the loaded burst.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        return burst.update_selected_portlets()

    @cherrypy.expose
    @handle_error(redirect=False)
    def get_visualizers_for_operation_id(self, op_id, width, height):
        """
        Method called from parameters exploration page in case a burst with a range of parameters
        for the simulator was launched. 
        :param op_id: the selected operation id from the parameter space exploration.
        :param width: the width of the right side display
        :param height: the height of the right side display
        
        Given these parameters first get the workflow to which op_id belongs, then load the portlets
        from that workflow as the current burst configuration. Width and height are used to get the
        proper sizes for the visualization iFrames. 
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        burst_config = self.burst_service.load_tab_configuration(
            burst_config, op_id)
        common.add2session(common.KEY_BURST_CONFIG, burst_config)
        return self.load_configured_visualizers(width, height)

    @expose_fragment("burst/portlets_view")
    def load_configured_visualizers(self, width='800', height='600'):
        """
        Load all the visualization steps for this tab. Width and height represent
        the dimensions of the right side Div, so that we can compute for each iFrame
        the maximum size it's visualizer can take.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab = burst.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst.tabs[int(selected_tab)].portlets:
            if portlet_cfg is not None:
                tab_portlet_list.append(
                    self.__portlet_config2portlet_entity(portlet_cfg))
        return {
            'status': burst.status,
            'portlet_tab_list': tab_portlet_list,
            'max_width': int(width),
            'max_height': int(height),
            'model': tvb.core.entities.model
        }

    @expose_fragment("burst/portlet_visualization_template")
    def check_status_for_visualizer(self,
                                    selected_tab,
                                    index_in_tab,
                                    width='800',
                                    height='600'):
        """
        This call is used to check on a regular basis if the data for a certain portlet is 
        available for visualization. Should return the status and the HTML to be displayed.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        target_portlet = burst.tabs[int(selected_tab)].portlets[int(
            index_in_tab)]
        target_portlet = self.__portlet_config2portlet_entity(target_portlet)
        template_dict = {
            'portlet_entity': target_portlet,
            'model': tvb.core.entities.model,
            'width': int(width),
            'height': int(height)
        }
        return template_dict

    @expose_json
    def reset_burst(self):
        """
        Called when click on "New Burst" entry happens from UI.
        This will generate an empty new Burst Configuration.
        """
        common.remove_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        new_burst = self.burst_service.new_burst_configuration(
            common.get_current_project().id)
        common.add2session(common.KEY_BURST_CONFIG, new_burst)

    @cherrypy.expose
    @handle_error(redirect=False)
    def copy_burst(self, burst_id):
        """
        When currently selected entry is a valid Burst, create a clone of that Burst.
        """
        common.remove_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        base_burst = self.burst_service.load_burst(burst_id)[0]
        if (base_burst is None) or (base_burst.id is None):
            return self.reset_burst()
        common.add2session(common.KEY_BURST_CONFIG, base_burst.clone())
        return base_burst.name

    @expose_fragment("burst/base_portlets_iframe")
    def launch_visualization(self, index_in_tab, frame_width, frame_height):
        """
        Launch the visualization for this tab and index in tab. The width and height represent the maximum of the inner 
        visualization canvas so that it can fit in the iFrame.
        """
        result = {}
        try:
            burst = common.get_from_session(common.KEY_BURST_CONFIG)
            visualizer = burst.tabs[burst.selected_tab].portlets[int(
                index_in_tab)].visualizer
            result = self.burst_service.launch_visualization(
                visualizer, float(frame_width), float(frame_height), True)[0]
            result['launch_success'] = True
        except Exception as ex:
            result['launch_success'] = False
            result['error_msg'] = str(ex)
            self.logger.exception("Could not launch Portlet Visualizer...")

        return self.fill_default_attributes(result)

    @expose_fragment("flow/genericAdapterFormFields")
    def configure_simulator_parameters(self):
        """
        Return the required input tree to generate the simulator interface for
        the burst page in 'configuration mode', meaning with checkboxes next to
        each input that are checked or not depending on if the user selected 
        them so, and with the user filled defaults.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        default_values, any_checked = burst_config.get_all_simulator_values()
        simulator_input_tree = self.cached_simulator_input_tree
        simulator_input_tree = InputTreeManager.fill_defaults(
            simulator_input_tree, default_values)
        ### Add simulator tree to session to be available in filters
        self.context.add_adapter_to_session(self.cached_simulator_algorithm,
                                            simulator_input_tree,
                                            default_values)

        template_vars = {}
        self.fill_default_attributes(template_vars)
        template_vars.update({
            "inputList":
            simulator_input_tree,
            common.KEY_PARAMETERS_CONFIG:
            True,
            'none_checked':
            not any_checked,
            'selectedParametersDictionary':
            burst_config.simulator_configuration
        })
        return template_vars

    @expose_fragment("flow/genericAdapterFormFields")
    def get_reduced_simulator_interface(self):
        """
        Get a simulator interface that only contains the inputs that are marked
        as KEY_PARAMETER_CHECKED in the current session.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        simulator_config = burst_config.simulator_configuration
        ## Fill with stored defaults, and see if any parameter was checked by user ##
        default_values, any_checked = burst_config.get_all_simulator_values()
        simulator_input_tree = self.cached_simulator_input_tree
        simulator_input_tree = InputTreeManager.fill_defaults(
            simulator_input_tree, default_values)
        ## In case no values were checked just skip tree-cut part and show entire simulator tree ##
        if any_checked:
            simulator_input_tree = InputTreeManager.select_simulator_inputs(
                simulator_input_tree, simulator_config)

        ### Add simulator tree to session to be available in filters
        self.context.add_adapter_to_session(self.cached_simulator_algorithm,
                                            simulator_input_tree,
                                            default_values)

        template_specification = {
            "inputList": simulator_input_tree,
            common.KEY_PARAMETERS_CONFIG: False,
            'draw_hidden_ranges': True
        }
        return self.fill_default_attributes(template_specification)

    @expose_json
    def get_previous_selected_rangers(self):
        """
        Retrieve Rangers, if any previously selected in Burst.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        first_range, second_range = '0', '0'
        if burst_config is not None:
            first_range = burst_config.get_simulation_parameter_value(
                RANGE_PARAMETER_1) or '0'
            second_range = burst_config.get_simulation_parameter_value(
                RANGE_PARAMETER_2) or '0'
        return [first_range, second_range]

    @expose_json
    def save_simulator_configuration(self, exclude_ranges, **data):
        """
        :param exclude_ranges: should be a boolean value. If it is True than the
            ranges will be excluded from the simulation parameters.

        Data is a dictionary with pairs in one of the forms:
            { 'simulator_parameters' : { $name$ : { 'value' : $value$, 'is_disabled' : true/false } },
              'burstName': $burst_name}
        
        The names for the checkboxes next to the parameter with name $name$ is always $name$_checked
        Save this dictionary in an easy to process form from which you could
        rebuild either only the selected entries, or all of the simulator tree
        with the given default values.
        """
        exclude_ranges = string2bool(str(exclude_ranges))
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        if BURST_NAME in data:
            burst_config.name = data[BURST_NAME]
        data = json.loads(data['simulator_parameters'])
        for entry in data:
            if exclude_ranges and (entry.endswith("_checked")
                                   or entry == RANGE_PARAMETER_1
                                   or entry == RANGE_PARAMETER_2):
                continue
            burst_config.update_simulation_parameter(entry, data[entry])
            checkbox_for_entry = entry + "_checked"
            if checkbox_for_entry in data:
                burst_config.update_simulation_parameter(
                    entry, data[checkbox_for_entry], KEY_PARAMETER_CHECKED)

    @expose_page
    @context_selected
    @settings
    def launch_full_visualizer(self, index_in_tab):
        """
        Launch the full scale visualizer from a small preview from the burst cockpit.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab = burst.selected_tab
        visualizer = burst.tabs[selected_tab].portlets[int(
            index_in_tab)].visualizer
        result, input_data = self.burst_service.launch_visualization(
            visualizer, is_preview=False)
        algorithm = self.flow_service.get_algorithm_by_identifier(
            visualizer.fk_algorithm)

        if common.KEY_TITLE not in result:
            result[common.KEY_TITLE] = algorithm.displayname

        result[common.KEY_ADAPTER] = algorithm.id
        result[common.KEY_OPERATION_ID] = None
        result[common.KEY_INCLUDE_RESOURCES] = 'flow/included_resources'
        ## Add required field to input dictionary and return it so that it can be used ##
        ## for top right control.                                                    ####
        input_data[common.KEY_ADAPTER] = algorithm.id

        if common.KEY_PARENT_DIV not in result:
            result[common.KEY_PARENT_DIV] = ''
        self.context.add_adapter_to_session(algorithm, None,
                                            copy.deepcopy(input_data))

        self._populate_section(algorithm, result, True)
        result[common.KEY_DISPLAY_MENU] = True
        result[common.KEY_BACK_PAGE] = "/burst"
        result[common.KEY_SUBMIT_LINK] = self.get_url_adapter(
            algorithm.fk_category, algorithm.id, 'burst')
        if KEY_CONTROLLS not in result:
            result[KEY_CONTROLLS] = ''
        return self.fill_default_attributes(result)

    def __portlet_config2portlet_entity(self, portlet_cfg):
        """
        From a portlet configuration as it is stored in session, update status and add the index in 
        tab so we can properly display it in the burst page.
        """
        portlet_entity = self.burst_service.get_portlet_by_id(
            portlet_cfg.portlet_id)
        portlet_status, error_msg = self.burst_service.get_portlet_status(
            portlet_cfg)
        portlet_entity.error_msg = error_msg
        portlet_entity.status = portlet_status
        portlet_entity.name = portlet_cfg.name
        portlet_entity.index_in_tab = portlet_cfg.index_in_tab
        portlet_entity.td_gid = generate_guid()
        return portlet_entity

    def _is_burst_name_ok(self, burst_name):
        """
        Validate a new burst name, to have only plain text.
        :returns: True, when validation succeeds, and an error message otherwise.
        """
        try:
            form = BurstNameForm()
            form.to_python({'burst_name': burst_name})
            return True
        except formencode.Invalid:
            validation_error = "Invalid simulation name %s. Please use only letters, numbers, or _ " % str(
                burst_name)
            self.logger.exception(validation_error)
            return validation_error

    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def export(self, burst_id):
        export_manager = ExportManager()
        export_json = export_manager.export_burst(burst_id)

        result_name = "tvb_simulation_" + str(burst_id) + ".json"
        return serve_fileobj(export_json, "application/x-download",
                             "attachment", result_name)

    @expose_fragment("overlay")
    def get_upload_overlay(self):
        template_specification = self.fill_overlay_attributes(
            None, "Upload", "Simulation JSON", "burst/upload_burst_overlay",
            "dialog-upload")
        return self.fill_default_attributes(template_specification)

    @cherrypy.expose
    @handle_error(redirect=True)
    @check_user
    @settings
    def load_burst_from_json(self, **data):
        """Upload Burst from previously exported JSON file"""
        self.logger.debug("Uploading ..." + str(data))

        try:
            upload_param = "uploadedfile"
            if upload_param in data and data[upload_param]:

                upload_param = data[upload_param]
                if isinstance(upload_param, FieldStorage) or isinstance(
                        upload_param, Part):
                    if not upload_param.file:
                        raise BurstServiceException(
                            "Please select a valid JSON file.")
                    upload_param = upload_param.file.read()

                upload_param = json.loads(upload_param)
                prj_id = common.get_current_project().id
                importer = ImportService()
                burst_entity = importer.load_burst_entity(upload_param, prj_id)
                common.add2session(common.KEY_BURST_CONFIG, burst_entity)

        except Exception as excep:
            self.logger.warning(excep.message)
            common.set_error_message(excep.message)

        raise cherrypy.HTTPRedirect('/burst/')
コード例 #4
0
class BurstController(BurstBaseController):
    """
    Controller class for Burst-Pages.
    """


    def __init__(self):
        BurstBaseController.__init__(self)
        self.burst_service = BurstService()
        self.workflow_service = WorkflowService()
        self.context = SelectedAdapterContext()

        ## Cache simulator Tree and Algorithm for performance issues.
        self.cached_simulator_algorithm = self.flow_service.get_algorithm_by_module_and_class(SIMULATOR_MODULE,
                                                                                              SIMULATOR_CLASS)


    @property
    @context_selected
    def cached_simulator_input_tree(self):
        """
        Cache Simulator's input tree, for performance issues.
        Anyway, without restart, the introspected tree will not be different on multiple executions.
        :returns: Simulator's Input Tree (copy from cache or just loaded)
        """
        cached_simulator_tree = common.get_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        if cached_simulator_tree is None:
            cached_simulator_tree = self.flow_service.prepare_adapter(common.get_current_project().id,
                                                                      self.cached_simulator_algorithm)
            common.add2session(common.KEY_CACHED_SIMULATOR_TREE, cached_simulator_tree)
        return copy.deepcopy(cached_simulator_tree)


    @expose_page
    @settings
    @context_selected
    def index(self):
        """Get on burst main page"""
        # todo : reuse load_burst here for consistency.
        template_specification = dict(mainContent="burst/main_burst", title="Simulation Cockpit",
                                      baseUrl=TvbProfile.current.web.BASE_URL,
                                      includedResources='project/included_resources')
        portlets_list = self.burst_service.get_available_portlets()
        session_stored_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_stored_burst is None or session_stored_burst.id is None:
            if session_stored_burst is None:
                session_stored_burst = self.burst_service.new_burst_configuration(common.get_current_project().id)
                common.add2session(common.KEY_BURST_CONFIG, session_stored_burst)

            adapter_interface = self.cached_simulator_input_tree
            if session_stored_burst is not None:
                current_data = session_stored_burst.get_all_simulator_values()[0]
                adapter_interface = InputTreeManager.fill_defaults(adapter_interface, current_data, True)
                ### Add simulator tree to session to be available in filters
                self.context.add_adapter_to_session(self.cached_simulator_algorithm, adapter_interface, current_data)
            template_specification['inputList'] = adapter_interface

        selected_portlets = session_stored_burst.update_selected_portlets()
        template_specification['burst_list'] = self.burst_service.get_available_bursts(common.get_current_project().id)
        template_specification['portletList'] = portlets_list
        template_specification['selectedPortlets'] = json.dumps(selected_portlets)
        template_specification['draw_hidden_ranges'] = True
        template_specification['burstConfig'] = session_stored_burst

        ### Prepare PSE available metrics
        ### We put here all available algorithms, because the metrics select area is a generic one, 
        ### and not loaded with every Burst Group change in history.
        algorithm = self.flow_service.get_algorithm_by_module_and_class(MEASURE_METRICS_MODULE, MEASURE_METRICS_CLASS)
        adapter_instance = ABCAdapter.build_adapter(algorithm)
        if adapter_instance is not None and hasattr(adapter_instance, 'available_algorithms'):
            template_specification['available_metrics'] = [metric_name for metric_name
                                                           in adapter_instance.available_algorithms.keys()]
        else:
            template_specification['available_metrics'] = []

        template_specification[common.KEY_PARAMETERS_CONFIG] = False
        template_specification[common.KEY_SECTION] = 'burst'
        return self.fill_default_attributes(template_specification)


    @expose_fragment('burst/burst_history')
    def load_burst_history(self):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        bursts = self.burst_service.get_available_bursts(common.get_current_project().id)
        self.burst_service.populate_burst_disk_usage(bursts)
        return {'burst_list': bursts,
                'selectedBurst': session_burst.id}


    @cherrypy.expose
    @handle_error(redirect=False)
    def get_selected_burst(self):
        """
        Return the burst that is currently stored in session.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_burst.id:
            return str(session_burst.id)
        else:
            return 'None'


    @expose_fragment('burst/portlet_configure_parameters')
    def get_portlet_configurable_interface(self, index_in_tab):
        """
        From the position given by the tab index and the index from that tab, 
        get the portlet configuration and build the configurable interface
        for that portlet.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_index = burst_config.selected_tab
        portlet_config = burst_config.tabs[tab_index].portlets[int(index_in_tab)]
        portlet_interface = self.burst_service.build_portlet_interface(portlet_config, common.get_current_project().id)

        full_portlet_input_tree = []
        for entry in portlet_interface:
            full_portlet_input_tree.extend(entry.interface)
        self.context.add_portlet_to_session(full_portlet_input_tree)

        portlet_interface = {"adapters_list": portlet_interface,
                             common.KEY_PARAMETERS_CONFIG: False,
                             common.KEY_SESSION_TREE: self.context.KEY_PORTLET_CONFIGURATION}
        return self.fill_default_attributes(portlet_interface)


    @expose_fragment('burst/portlets_preview')
    def portlet_tab_display(self, **data):
        """
        When saving a new configuration of tabs, check if any of the old 
        portlets are still present, and if that is the case use their 
        parameters configuration. 
        
        For all the new portlets add entries in the burst configuration. 
        Also remove old portlets that are no longer saved.
        """
        tab_portlets_list = json.loads(data['tab_portlets_list'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab_idx = burst_config.selected_tab
        for tab_idx in xrange(len(tab_portlets_list)):
            current_tab = burst_config.tabs[tab_idx]
            ### When configuration already exists, and new portlets          #####
            ### are selected, first check if any configuration was saved for #####
            ### each portlet and if that is the case, use it. If none is present #
            ### create a new one.                                              ###
            for idx_in_tab in xrange(len(tab_portlets_list[tab_idx])):
                portlet_id = tab_portlets_list[tab_idx][idx_in_tab][0]
                portlet_name = tab_portlets_list[tab_idx][idx_in_tab][1]
                if portlet_id >= 0:
                    saved_config = current_tab.portlets[idx_in_tab]
                    if saved_config is None or saved_config.portlet_id != portlet_id:
                        current_tab.portlets[idx_in_tab] = self.burst_service.new_portlet_configuration(portlet_id,
                                                                                    tab_idx, idx_in_tab, portlet_name)
                    else:
                        saved_config.visualizer.ui_name = portlet_name
                else:
                    current_tab.portlets[idx_in_tab] = None
            #For generating the HTML get for each id the corresponding portlet
        selected_tab_portlets = []
        saved_selected_tab = burst_config.tabs[selected_tab_idx]
        for portlet in saved_selected_tab.portlets:
            if portlet:
                portlet_id = int(portlet.portlet_id)
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_id)
                portlet_entity.name = portlet.name
                selected_tab_portlets.append(portlet_entity)

        return {'portlet_tab_list': selected_tab_portlets}


    @expose_fragment('burst/portlets_preview')
    def get_configured_portlets(self):
        """
        Return the portlets for one given tab. This is used when changing
        from tab to tab and selecting which portlets will be displayed.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        if burst_config is None:
            return {'portlet_tab_list': []}

        tab_idx = burst_config.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst_config.tabs[int(tab_idx)].portlets:
            if portlet_cfg is not None:
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_cfg.portlet_id)
                portlet_entity.name = portlet_cfg.name
                tab_portlet_list.append(portlet_entity)
        return {'portlet_tab_list': tab_portlet_list}


    @expose_json
    def change_selected_tab(self, tab_nr):
        """
        Set :param tab_nr: as the currently selected tab in the stored burst
        configuration. 
        """
        common.get_from_session(common.KEY_BURST_CONFIG).selected_tab = int(tab_nr)


    @expose_json
    def get_portlet_session_configuration(self):
        """
        Get the current configuration of portlets stored in session for this burst,
        as a json.
        """
        burst_entity = common.get_from_session(common.KEY_BURST_CONFIG)
        returned_configuration = burst_entity.update_selected_portlets()
        return returned_configuration


    @cherrypy.expose
    @handle_error(redirect=False)
    def save_parameters(self, index_in_tab, **data):
        """
        Save parameters
        
        :param tab_nr: the index of the selected tab
        :param index_in_tab: the index of the configured portlet in the selected tab
        :param data: the {"portlet_parameters": json_string} Where json_string is a Jsonified dictionary
            {"name": value}, representing the configuration of the current portlet
        
        Having these inputs, current method updated the configuration of the portlet in the
        corresponding tab position form the burst configuration in session.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_nr = burst_config.selected_tab
        old_portlet_config = burst_config.tabs[int(tab_nr)].portlets[int(index_in_tab)]
        data = json.loads(data['portlet_parameters'])

        # Replace all void entries with 'None'
        for entry in data:
            if data[entry] == '':
                data[entry] = None

        need_relaunch = self.burst_service.update_portlet_configuration(old_portlet_config, data)
        if need_relaunch:
            #### Reset Burst Configuration into an entity not persisted (id = None for all)
            common.add2session(common.KEY_BURST_CONFIG, burst_config.clone())
            return "relaunchView"
        else:
            self.workflow_service.store_workflow_step(old_portlet_config.visualizer)
            return "noRelaunch"


    @expose_json
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        validation_result = self._is_burst_name_ok(burst_name)
        if validation_result is True:
            self.burst_service.rename_burst(burst_id, burst_name)
            return {'success': "Simulation successfully renamed!"}
        else:
            return {'error': validation_result}


    @expose_json
    def launch_burst(self, launch_mode, burst_name, **data):
        """
        Do the actual burst launch, using the configuration saved in current session.
        :param launch_mode: new/branch/continue
        :param burst_name: user-given burst name. It can be empty (case in which we will fill with simulation_x)
        :param data: kwargs for simulation input parameters.
        """
        data = json.loads(data['simulator_parameters'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)

        ## Validate new burst-name
        if launch_mode == LAUNCH_NEW and burst_name != 'none_undefined':
            validation_result = self._is_burst_name_ok(burst_name)
            if validation_result is True:
                burst_config.name = burst_name
            else:
                return {'error': validation_result}

        ## Fill all parameters 
        user_id = common.get_logged_user().id
        data[common.KEY_ADAPTER] = self.cached_simulator_algorithm.id
        burst_config.update_simulator_configuration(data)
        burst_config.fk_project = common.get_current_project().id

        ## Do the asynchronous launch
        try:
            burst_id, burst_name = self.burst_service.launch_burst(burst_config, 0, self.cached_simulator_algorithm.id,
                                                                   user_id, launch_mode)
            return {'id': burst_id, 'name': burst_name}
        except BurstServiceException, e:
            self.logger.exception("Could not launch burst!")
            return {'error': e.message}
コード例 #5
0
class BurstController(BurstBaseController):
    """
    Controller class for Burst-Pages.
    """


    def __init__(self):
        BurstBaseController.__init__(self)
        self.burst_service = BurstService()
        self.workflow_service = WorkflowService()
        self.context = SelectedAdapterContext()

        ## Cache simulator Tree, Algorithm and AlgorithmGroup, for performance issues.
        algorithm, self.cached_simulator_algo_group = self.flow_service.get_algorithm_by_module_and_class(
            SIMULATOR_MODULE, SIMULATOR_CLASS)
        self.cached_simulator_algorithm_id = algorithm.id


    @property
    @context_selected
    def cached_simulator_input_tree(self):
        """
        Cache Simulator's input tree, for performance issues.
        Anyway, without restart, the introspected tree will not be different on multiple executions.
        :returns: Simulator's Input Tree (copy from cache or just loaded)
        """
        cached_simulator_tree = common.get_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        if cached_simulator_tree is None:
            cached_simulator_tree = self.flow_service.prepare_adapter(common.get_current_project().id,
                                                                      self.cached_simulator_algo_group)[1]
            common.add2session(common.KEY_CACHED_SIMULATOR_TREE, cached_simulator_tree)
        return copy.deepcopy(cached_simulator_tree)


    @expose_page
    @settings
    @context_selected
    def index(self):
        """Get on burst main page"""
        # todo : reuse load_burst here for consistency.
        template_specification = dict(mainContent="burst/main_burst", title="Simulation Cockpit",
                                      baseUrl=TvbProfile.current.web.BASE_URL,
                                      includedResources='project/included_resources')
        portlets_list = self.burst_service.get_available_portlets()
        session_stored_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_stored_burst is None or session_stored_burst.id is None:
            if session_stored_burst is None:
                session_stored_burst = self.burst_service.new_burst_configuration(common.get_current_project().id)
                common.add2session(common.KEY_BURST_CONFIG, session_stored_burst)

            adapter_interface = self.cached_simulator_input_tree
            if session_stored_burst is not None:
                current_data = session_stored_burst.get_all_simulator_values()[0]
                adapter_interface = ABCAdapter.fill_defaults(adapter_interface, current_data, True)
                ### Add simulator tree to session to be available in filters
                self.context.add_adapter_to_session(self.cached_simulator_algo_group, adapter_interface, current_data)
            template_specification['inputList'] = adapter_interface

        selected_portlets = session_stored_burst.update_selected_portlets()
        template_specification['burst_list'] = self.burst_service.get_available_bursts(common.get_current_project().id)
        template_specification['portletList'] = portlets_list
        template_specification['selectedPortlets'] = json.dumps(selected_portlets)
        template_specification['draw_hidden_ranges'] = True
        template_specification['burstConfig'] = session_stored_burst

        ### Prepare PSE available metrics
        ### We put here all available algorithms, because the metrics select area is a generic one, 
        ### and not loaded with every Burst Group change in history.
        algo_group = self.flow_service.get_algorithm_by_module_and_class(MEASURE_METRICS_MODULE,
                                                                         MEASURE_METRICS_CLASS)[1]
        adapter_instance = ABCAdapter.build_adapter(algo_group)
        if adapter_instance is not None and hasattr(adapter_instance, 'available_algorithms'):
            template_specification['available_metrics'] = [metric_name for metric_name
                                                           in adapter_instance.available_algorithms.keys()]
        else:
            template_specification['available_metrics'] = []

        template_specification[common.KEY_PARAMETERS_CONFIG] = False
        template_specification[common.KEY_SECTION] = 'burst'
        return self.fill_default_attributes(template_specification)


    @expose_fragment('burst/burst_history')
    def load_burst_history(self):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        return {'burst_list': self.burst_service.get_available_bursts(common.get_current_project().id),
                'selectedBurst': session_burst.id}


    @cherrypy.expose
    @handle_error(redirect=False)
    def get_selected_burst(self):
        """
        Return the burst that is currently stored in session.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        if session_burst.id:
            return str(session_burst.id)
        else:
            return 'None'


    @expose_fragment('burst/portlet_configure_parameters')
    def get_portlet_configurable_interface(self, index_in_tab):
        """
        From the position given by the tab index and the index from that tab, 
        get the portlet configuration and build the configurable interface
        for that portlet.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_index = burst_config.selected_tab
        portlet_config = burst_config.tabs[tab_index].portlets[int(index_in_tab)]
        portlet_interface = self.burst_service.build_portlet_interface(portlet_config, common.get_current_project().id)

        full_portlet_input_tree = []
        for entry in portlet_interface:
            full_portlet_input_tree.extend(entry.interface)
        self.context.add_portlet_to_session(full_portlet_input_tree)

        portlet_interface = {"adapters_list": portlet_interface,
                             common.KEY_PARAMETERS_CONFIG: False,
                             common.KEY_SESSION_TREE: self.context.KEY_PORTLET_CONFIGURATION}
        return self.fill_default_attributes(portlet_interface)


    @expose_fragment('burst/portlets_preview')
    def portlet_tab_display(self, **data):
        """
        When saving a new configuration of tabs, check if any of the old 
        portlets are still present, and if that is the case use their 
        parameters configuration. 
        
        For all the new portlets add entries in the burst configuration. 
        Also remove old portlets that are no longer saved.
        """
        tab_portlets_list = json.loads(data['tab_portlets_list'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab_idx = burst_config.selected_tab
        for tab_idx in xrange(len(tab_portlets_list)):
            current_tab = burst_config.tabs[tab_idx]
            ### When configuration already exists, and new portlets          #####
            ### are selected, first check if any configuration was saved for #####
            ### each portlet and if that is the case, use it. If none is present #
            ### create a new one.                                              ###
            for idx_in_tab in xrange(len(tab_portlets_list[tab_idx])):
                portlet_id = tab_portlets_list[tab_idx][idx_in_tab][0]
                portlet_name = tab_portlets_list[tab_idx][idx_in_tab][1]
                if portlet_id >= 0:
                    saved_config = current_tab.portlets[idx_in_tab]
                    if saved_config is None or saved_config.portlet_id != portlet_id:
                        current_tab.portlets[idx_in_tab] = self.burst_service.new_portlet_configuration(portlet_id,
                                                                                    tab_idx, idx_in_tab, portlet_name)
                    else:
                        saved_config.visualizer.ui_name = portlet_name
                else:
                    current_tab.portlets[idx_in_tab] = None
            #For generating the HTML get for each id the corresponding portlet
        selected_tab_portlets = []
        saved_selected_tab = burst_config.tabs[selected_tab_idx]
        for portlet in saved_selected_tab.portlets:
            if portlet:
                portlet_id = int(portlet.portlet_id)
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_id)
                portlet_entity.name = portlet.name
                selected_tab_portlets.append(portlet_entity)

        return {'portlet_tab_list': selected_tab_portlets}


    @expose_fragment('burst/portlets_preview')
    def get_configured_portlets(self):
        """
        Return the portlets for one given tab. This is used when changing
        from tab to tab and selecting which portlets will be displayed.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        if burst_config is None:
            return {'portlet_tab_list': []}

        tab_idx = burst_config.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst_config.tabs[int(tab_idx)].portlets:
            if portlet_cfg is not None:
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_cfg.portlet_id)
                portlet_entity.name = portlet_cfg.name
                tab_portlet_list.append(portlet_entity)
        return {'portlet_tab_list': tab_portlet_list}


    @expose_json
    def change_selected_tab(self, tab_nr):
        """
        Set :param tab_nr: as the currently selected tab in the stored burst
        configuration. 
        """
        common.get_from_session(common.KEY_BURST_CONFIG).selected_tab = int(tab_nr)


    @expose_json
    def get_portlet_session_configuration(self):
        """
        Get the current configuration of portlets stored in session for this burst,
        as a json.
        """
        burst_entity = common.get_from_session(common.KEY_BURST_CONFIG)
        returned_configuration = burst_entity.update_selected_portlets()
        return returned_configuration


    @cherrypy.expose
    @handle_error(redirect=False)
    def save_parameters(self, index_in_tab, **data):
        """
        Save parameters
        
        :param tab_nr: the index of the selected tab
        :param index_in_tab: the index of the configured portlet in the selected tab
        :param data: the {"portlet_parameters": json_string} Where json_string is a Jsonified dictionary
            {"name": value}, representing the configuration of the current portlet
        
        Having these inputs, current method updated the configuration of the portlet in the
        corresponding tab position form the burst configuration in session.
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        tab_nr = burst_config.selected_tab
        old_portlet_config = burst_config.tabs[int(tab_nr)].portlets[int(index_in_tab)]
        data = json.loads(data['portlet_parameters'])

        # Replace all void entries with 'None'
        for entry in data:
            if data[entry] == '':
                data[entry] = None

        need_relaunch = self.burst_service.update_portlet_configuration(old_portlet_config, data)
        if need_relaunch:
            #### Reset Burst Configuration into an entity not persisted (id = None for all)
            common.add2session(common.KEY_BURST_CONFIG, burst_config.clone())
            return "relaunchView"
        else:
            self.workflow_service.store_workflow_step(old_portlet_config.visualizer)
            return "noRelaunch"


    @expose_json
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        validation_result = self._is_burst_name_ok(burst_name)
        if validation_result is True:
            self.burst_service.rename_burst(burst_id, burst_name)
            return {'success': "Simulation successfully renamed!"}
        else:
            return {'error': validation_result}


    @expose_json
    def launch_burst(self, launch_mode, burst_name, **data):
        """
        Do the actual burst launch, using the configuration saved in current session.
        :param launch_mode: new/branch/continue
        :param burst_name: user-given burst name. It can be empty (case in which we will fill with simulation_x)
        :param data: kwargs for simulation input parameters.
        """
        data = json.loads(data['simulator_parameters'])
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)

        ## Validate new burst-name
        if launch_mode == LAUNCH_NEW and burst_name != 'none_undefined':
            validation_result = self._is_burst_name_ok(burst_name)
            if validation_result is True:
                burst_config.name = burst_name
            else:
                return {'error': validation_result}

        ## Fill all parameters 
        user_id = common.get_logged_user().id
        data[common.KEY_ADAPTER] = self.cached_simulator_algorithm_id
        burst_config.update_simulator_configuration(data)
        burst_config.fk_project = common.get_current_project().id

        ## Do the asynchronous launch
        burst_id, burst_name = self.burst_service.launch_burst(burst_config, 0, self.cached_simulator_algorithm_id,
                                                               user_id, launch_mode)
        return {'id': burst_id, 'name': burst_name}


    @expose_json
    def load_burst(self, burst_id):
        """
        Given a burst id return its running status, weather it was a operation group and the selected tab.
        This is called when a burst is selected in the history,
        when returning from a burst config page (model param or noise)
        and when the status of running simulations is polled.
        Besides returning these values it updates the session stored burst.

        A burst configuration has 2 meanings.
        It is a staging configuration for a new burst (stored in transients in the session).
        It is the configuration used to launch a simulation and it's running status (stored in the db).
        This method has to merge the two meanings.
        If the requested burst_id is different from the one held in the session,
        then the burst config is loaded from the db, discarding any session stored config.
        If the id is the same then the session config is kept.
        """
        try:
            burst_id = int(burst_id)
            old_burst = common.get_from_session(common.KEY_BURST_CONFIG)
            burst, group_gid = self.burst_service.load_burst(burst_id)

            if old_burst.id == burst_id:
                # This function was called to reload the current burst.
                # Merge session config into the db config. Overwrite all transient fields
                burst.simulator_configuration = old_burst.simulator_configuration
                burst.dynamic_ids = old_burst.dynamic_ids

            burst.selected_tab = old_burst.selected_tab
            common.add2session(common.KEY_BURST_CONFIG, burst)
            return {'status': burst.status, 'group_gid': group_gid, 'selected_tab': burst.selected_tab}
        except Exception:
            ### Most probably Burst was removed. Delete it from session, so that client 
            ### has a good chance to get a good response on refresh
            self.logger.exception("Error loading burst")
            common.remove_from_session(common.KEY_BURST_CONFIG)
            raise


    @expose_json
    def get_history_status(self, **data):
        """
        For each burst id received, get the status and return it.
        """
        return self.burst_service.update_history_status(json.loads(data['burst_ids']))


    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def cancel_or_remove_burst(self, burst_id):
        """
        Cancel or Remove the burst entity given by burst_id.
        :returns 'reset-new': When currently selected burst was removed. JS will need to reset selection to a new entry
        :returns 'canceled': When current burst was still running and was just stopped.
        :returns 'done': When no action is required on the client.
        """
        burst_id = int(burst_id)
        session_burst = common.get_from_session(common.KEY_BURST_CONFIG)
        removed = self.burst_service.cancel_or_remove_burst(burst_id)
        if removed:
            if session_burst.id == burst_id:
                return "reset-new"
            return 'done'
        else:
            # Burst was stopped since it was running
            return 'canceled'


    @expose_json
    def get_selected_portlets(self):
        """
        Get the selected portlets for the loaded burst.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        return burst.update_selected_portlets()


    @cherrypy.expose
    @handle_error(redirect=False)
    def get_visualizers_for_operation_id(self, op_id, width, height):
        """
        Method called from parameters exploration page in case a burst with a range of parameters
        for the simulator was launched. 
        :param op_id: the selected operation id from the parameter space exploration.
        :param width: the width of the right side display
        :param height: the height of the right side display
        
        Given these parameters first get the workflow to which op_id belongs, then load the portlets
        from that workflow as the current burst configuration. Width and height are used to get the
        proper sizes for the visualization iFrames. 
        """
        burst_config = common.get_from_session(common.KEY_BURST_CONFIG)
        burst_config = self.burst_service.load_tab_configuration(burst_config, op_id)
        common.add2session(common.KEY_BURST_CONFIG, burst_config)
        return self.load_configured_visualizers(width, height)


    @expose_fragment("burst/portlets_view")
    def load_configured_visualizers(self, width='800', height='600'):
        """
        Load all the visualization steps for this tab. Width and height represent
        the dimensions of the right side Div, so that we can compute for each iFrame
        the maximum size it's visualizer can take.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        selected_tab = burst.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst.tabs[int(selected_tab)].portlets:
            if portlet_cfg is not None:
                tab_portlet_list.append(self.__portlet_config2portlet_entity(portlet_cfg))
        return {'status': burst.status, 'portlet_tab_list': tab_portlet_list,
                'max_width': int(width), 'max_height': int(height), 'model': tvb.core.entities.model}


    @expose_fragment("burst/portlet_visualization_template")
    def check_status_for_visualizer(self, selected_tab, index_in_tab, width='800', height='600'):
        """
        This call is used to check on a regular basis if the data for a certain portlet is 
        available for visualization. Should return the status and the HTML to be displayed.
        """
        burst = common.get_from_session(common.KEY_BURST_CONFIG)
        target_portlet = burst.tabs[int(selected_tab)].portlets[int(index_in_tab)]
        target_portlet = self.__portlet_config2portlet_entity(target_portlet)
        template_dict = {'portlet_entity': target_portlet, 'model': tvb.core.entities.model,
                         'width': int(width), 'height': int(height)}
        return template_dict


    @expose_json
    def reset_burst(self):
        """
        Called when click on "New Burst" entry happens from UI.
        This will generate an empty new Burst Configuration.
        """
        common.remove_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        new_burst = self.burst_service.new_burst_configuration(common.get_current_project().id)
        common.add2session(common.KEY_BURST_CONFIG, new_burst)


    @cherrypy.expose
    @handle_error(redirect=False)
    def copy_burst(self, burst_id):
        """
        When currently selected entry is a valid Burst, create a clone of that Burst.
        """
        common.remove_from_session(common.KEY_CACHED_SIMULATOR_TREE)
        base_burst = self.burst_service.load_burst(burst_id)[0]
        if (base_burst is None) or (base_burst.id is None):
            return self.reset_burst()
        common.add2session(common.KEY_BURST_CONFIG, base_burst.clone())
        return base_burst.name


    @expose_fragment("burst/base_portlets_iframe")
    def launch_visualization(self, index_in_tab, frame_width, frame_height, method_name="generate_preview"):
        """
        Launch the visualization for this tab and index in tab. The width and height represent the maximum of the inner 
        visualization canvas so that it can fit in the iFrame.
        """
        result = {}
        try:
            burst = common.get_from_session(common.KEY_BURST_CONFIG)
            visualizer = burst.tabs[burst.selected_tab].portlets[int(index_in_tab)].visualizer
            result = self.burst_service.launch_visualization(visualizer, float(frame_width),
                                                             float(frame_height), method_name)[0]
            result['launch_success'] = True
        except Exception, ex:
            result['launch_success'] = False
            result['error_msg'] = str(ex)
            self.logger.exception("Could not launch Portlet Visualizer...")

        return self.fill_default_attributes(result)
コード例 #6
0
class BurstController(base.BaseController):
    """
    Controller class for Burst-Pages.
    """


    def __init__(self):
        base.BaseController.__init__(self)
        self.burst_service = BurstService()
        self.workflow_service = WorkflowService()
        self.context = SelectedAdapterContext()

        ## Cache simulator Tree, Algorithm and AlgorithmGroup, for performance issues.
        algorithm, self.cached_simulator_algo_group = self.flow_service.get_algorithm_by_module_and_class(
            SIMULATOR_MODULE, SIMULATOR_CLASS)
        self.cached_simulator_algorithm_id = algorithm.id


    @property
    @context_selected()
    def cached_simulator_input_tree(self):
        """
        Cache Simulator's input tree, for performance issues.
        Anyway, without restart, the introspected tree will not be different on multiple executions.
        :returns: Simulator's Input Tree (copy from cache or just loaded)
        """
        cached_simulator_tree = base.get_from_session(base.KEY_CACHED_SIMULATOR_TREE)
        if cached_simulator_tree is None:
            cached_simulator_tree = self.flow_service.prepare_adapter(base.get_current_project().id,
                                                                      self.cached_simulator_algo_group)[1]
            base.add2session(base.KEY_CACHED_SIMULATOR_TREE, cached_simulator_tree)
        return copy.deepcopy(cached_simulator_tree)


    @cherrypy.expose
    @using_template('base_template')
    @base.settings()
    @logged()
    @context_selected()
    def index(self):
        """Get on burst main page"""
        template_specification = dict(mainContent="burst/main_burst", title="Simulation Cockpit",
                                      baseUrl=cfg.BASE_URL, includedResources='project/included_resources')
        portlets_list = self.burst_service.get_available_portlets()
        session_stored_burst = base.get_from_session(base.KEY_BURST_CONFIG)
        if session_stored_burst is None or session_stored_burst.id is None:
            if session_stored_burst is None:
                session_stored_burst = self.burst_service.new_burst_configuration(base.get_current_project().id)
                base.add2session(base.KEY_BURST_CONFIG, session_stored_burst)

            adapter_interface = self.cached_simulator_input_tree
            if session_stored_burst is not None:
                current_data = session_stored_burst.get_all_simulator_values()[0]
                adapter_interface = ABCAdapter.fill_defaults(adapter_interface, current_data, True)
                ### Add simulator tree to session to be available in filters
                self.context.add_adapter_to_session(self.cached_simulator_algo_group, adapter_interface, current_data)
            template_specification['inputList'] = adapter_interface

        selected_portlets = session_stored_burst.update_selected_portlets()
        template_specification['burst_list'] = self.burst_service.get_available_bursts(base.get_current_project().id)
        template_specification['portletList'] = portlets_list
        template_specification['selectedPortlets'] = json.dumps(selected_portlets)
        template_specification['draw_hidden_ranges'] = True
        template_specification['burstConfig'] = session_stored_burst

        ### Prepare PSE available metrics
        ### We put here all available algorithms, because the metrics select area is a generic one, 
        ### and not loaded with every Burst Group change in history.
        algo_group = self.flow_service.get_algorithm_by_module_and_class(MEASURE_METRICS_MODULE,
                                                                         MEASURE_METRICS_CLASS)[1]
        adapter_instance = ABCAdapter.build_adapter(algo_group)
        if adapter_instance is not None and hasattr(adapter_instance, 'available_algorithms'):
            template_specification['available_metrics'] = [metric_name for metric_name
                                                           in adapter_instance.available_algorithms.keys()]
        else:
            template_specification['available_metrics'] = []

        template_specification[base.KEY_PARAMETERS_CONFIG] = False
        template_specification[base.KEY_SECTION] = 'burst'
        return self.fill_default_attributes(template_specification)


    @cherrypy.expose
    @using_template('burst/burst_history')
    def load_burst_history(self):
        """
        Load the available burst that are stored in the database at this time.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = base.get_from_session(base.KEY_BURST_CONFIG)
        return {'burst_list': self.burst_service.get_available_bursts(base.get_current_project().id),
                'selectedBurst': session_burst.id}


    @cherrypy.expose
    @ajax_call(False)
    def get_selected_burst(self):
        """
        Return the burst that is currently stored in session.
        This is one alternative to 'chrome-back problem'.
        """
        session_burst = base.get_from_session(base.KEY_BURST_CONFIG)
        if session_burst.id:
            return str(session_burst.id)
        else:
            return 'None'


    @cherrypy.expose
    @using_template('burst/portlet_configure_parameters')
    def get_portlet_configurable_interface(self, index_in_tab):
        """
        From the position given by the tab index and the index from that tab, 
        get the portlet configuration and build the configurable interface
        for that portlet.
        """
        burst_config = base.get_from_session(base.KEY_BURST_CONFIG)
        tab_index = burst_config.selected_tab
        portlet_config = burst_config.tabs[tab_index].portlets[int(index_in_tab)]
        portlet_interface = self.burst_service.build_portlet_interface(portlet_config, base.get_current_project().id)

        full_portlet_input_tree = []
        for entry in portlet_interface:
            full_portlet_input_tree.extend(entry.interface)
        self.context.add_portlet_to_session(full_portlet_input_tree)

        portlet_interface = {"adapters_list": portlet_interface,
                             base.KEY_PARAMETERS_CONFIG: False,
                             base.KEY_SESSION_TREE: self.context.KEY_PORTLET_CONFIGURATION}
        return self.fill_default_attributes(portlet_interface)


    @cherrypy.expose
    @using_template('burst/portlets_preview')
    def portlet_tab_display(self, **data):
        """
        When saving a new configuration of tabs, check if any of the old 
        portlets are still present, and if that is the case use their 
        parameters configuration. 
        
        For all the new portlets add entries in the burst configuration. 
        Also remove old portlets that are no longer saved.
        """
        tab_portlets_list = json.loads(data['tab_portlets_list'])
        burst_config = base.get_from_session(base.KEY_BURST_CONFIG)
        selected_tab_idx = burst_config.selected_tab
        for tab_idx in xrange(len(tab_portlets_list)):
            current_tab = burst_config.tabs[tab_idx]
            ### When configuration already exists, and new portlets          #####
            ### are selected, first check if any configuration was saved for #####
            ### each portlet and if that is the case, use it. If none is present #
            ### create a new one.                                              ###
            for idx_in_tab in xrange(len(tab_portlets_list[tab_idx])):
                portlet_id = tab_portlets_list[tab_idx][idx_in_tab][0]
                portlet_name = tab_portlets_list[tab_idx][idx_in_tab][1]
                if portlet_id >= 0:
                    saved_config = current_tab.portlets[idx_in_tab]
                    if saved_config is None or saved_config.portlet_id != portlet_id:
                        current_tab.portlets[idx_in_tab] = self.burst_service.new_portlet_configuration(portlet_id,
                                                                                    tab_idx, idx_in_tab, portlet_name)
                    else:
                        saved_config.visualizer.ui_name = portlet_name
                else:
                    current_tab.portlets[idx_in_tab] = None
            #For generating the HTML get for each id the corresponding portlet
        selected_tab_portlets = []
        saved_selected_tab = burst_config.tabs[selected_tab_idx]
        for portlet in saved_selected_tab.portlets:
            if portlet:
                portlet_id = int(portlet.portlet_id)
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_id)
                portlet_entity.name = portlet.name
                selected_tab_portlets.append(portlet_entity)

        return {'portlet_tab_list': selected_tab_portlets}


    @cherrypy.expose
    @using_template('burst/portlets_preview')
    def get_configured_portlets(self):
        """
        Return the portlets for one given tab. This is used when changing
        from tab to tab and selecting which portlets will be displayed.
        """
        burst_config = base.get_from_session(base.KEY_BURST_CONFIG)
        if burst_config is None:
            return {'portlet_tab_list': []}

        tab_idx = burst_config.selected_tab
        tab_portlet_list = []
        for portlet_cfg in burst_config.tabs[int(tab_idx)].portlets:
            if portlet_cfg is not None:
                portlet_entity = self.burst_service.get_portlet_by_id(portlet_cfg.portlet_id)
                portlet_entity.name = portlet_cfg.name
                tab_portlet_list.append(portlet_entity)
        return {'portlet_tab_list': tab_portlet_list}


    @cherrypy.expose
    @ajax_call()
    def change_selected_tab(self, tab_nr):
        """
        Set :param tab_nr: as the currently selected tab in the stored burst
        configuration. 
        """
        base.get_from_session(base.KEY_BURST_CONFIG).selected_tab = int(tab_nr)


    @cherrypy.expose
    @ajax_call()
    def get_portlet_session_configuration(self):
        """
        Get the current configuration of portlets stored in session for this burst,
        as a json.
        """
        burst_entity = base.get_from_session(base.KEY_BURST_CONFIG)
        returned_configuration = burst_entity.update_selected_portlets()
        return returned_configuration


    @cherrypy.expose
    @ajax_call(False)
    def save_parameters(self, index_in_tab, **data):
        """
        Save parameters
        
        :param tab_nr: the index of the selected tab
        :param index_in_tab: the index of the configured portlet in the selected tab
        :param data: the {name:value} dictionary configuration of the current portlet
        
        Having these inputs, update the configuration of the portletin the 
        corresponding tab position form the burst configuration .
        """
        burst_config = base.get_from_session(base.KEY_BURST_CONFIG)
        tab_nr = burst_config.selected_tab
        old_portlet_config = burst_config.tabs[int(tab_nr)].portlets[int(index_in_tab)]

        # Replace all void entries with 'None'
        for entry in data:
            if data[entry] == '':
                data[entry] = None

        need_relaunch = self.burst_service.update_portlet_configuration(old_portlet_config, data)
        if need_relaunch:
            #### Reset Burst Configuration into an entity not persisted (id = None for all)
            base.add2session(base.KEY_BURST_CONFIG, burst_config.clone())
            return "relaunchView"
        else:
            self.workflow_service.store_workflow_step(old_portlet_config.visualizer)
            return "noRelaunch"


    @cherrypy.expose
    @ajax_call()
    def rename_burst(self, burst_id, burst_name):
        """
        Rename the burst given by burst_id, setting it's new name to
        burst_name.
        """
        self._validate_burst_name(burst_name)
        self.burst_service.rename_burst(burst_id, burst_name)


    @cherrypy.expose
    @ajax_call()
    def launch_burst(self, launch_mode, burst_name, **data):
        """
        Do the actual burst launch, using the configuration saved in current session.
        :param launch_mode: new/branch/continue
        :param burst_name: user-given burst name. It can be empty (case in which we will fill with simulation_x)
        :param data: kwargs for simulation input parameters.
        """
        burst_config = base.get_from_session(base.KEY_BURST_CONFIG)

        ## Validate new burst-name
        if burst_name != 'none_undefined':
            self._validate_burst_name(burst_name)
            burst_config.name = burst_name

        ## Fill all parameters 
        user_id = base.get_logged_user().id
        data[base.KEY_ADAPTER] = self.cached_simulator_algorithm_id
        burst_config.update_simulator_configuration(data)
        burst_config.fk_project = base.get_current_project().id

        ## Do the asynchronous launch
        burst_id, burst_name = self.burst_service.launch_burst(burst_config, 0, self.cached_simulator_algorithm_id,
                                                               user_id, launch_mode)
        return [burst_id, burst_name]


    @cherrypy.expose
    @ajax_call()
    def load_burst(self, burst_id):
        """
        Given a clicked burst from the history and the selected tab, load all 
        the required data from that burst. Return a value specifying if it was a result
        of a range launch (OperationGroup) or not.
        """
        try:
            old_burst = base.get_from_session(base.KEY_BURST_CONFIG)
            burst, group_gid = self.burst_service.load_burst(burst_id)
            burst.selected_tab = old_burst.selected_tab
            base.add2session(base.KEY_BURST_CONFIG, burst)
            return {'status': burst.status, 'group_gid': group_gid, 'selected_tab': burst.selected_tab}
        except Exception, excep:
            ### Most probably Burst was removed. Delete it from session, so that client 
            ### has a good chance to get a good response on refresh
            self.logger.error(excep)
            base.remove_from_session(base.KEY_BURST_CONFIG)
            raise excep