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/')
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}
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/')
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}
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)
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