Esempio n. 1
0
    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()
        self.operation_services = OperationService()
        self.simulator_controller = SimulatorController()

        analyze_category, groups = self.algorithm_service.get_analyze_groups()
        adapters_list = []
        for adapter_group in groups:

            if len(adapter_group.children) > 1:
                ids = [str(child.id) for child in adapter_group.children]
                ids = ','.join(ids)
                adapter_link = '/flow/show_group_of_algorithms/' + str(
                    analyze_category.id) + "/" + ids
            else:
                adapter_link = self.get_url_adapter(
                    analyze_category.id, adapter_group.children[0].id)

            adapters_list.append({
                common.KEY_TITLE:
                adapter_group.name,
                'link':
                adapter_link,
                'description':
                adapter_group.description,
                'subsection':
                adapter_group.children[0].subsection_name
            })
        self.analyze_adapters = adapters_list
Esempio n. 2
0
 def __init__(self):
     BaseController.__init__(self)
     self.context = SelectedAdapterContext()
     self.files_helper = FilesHelper()
Esempio n. 3
0
class FlowController(BaseController):
    """
    This class takes care of executing steps in projects.
    """
    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()

    @expose_page
    @settings
    @context_selected
    def step_analyzers(self):
        """
        Choose exact action/adapter for current step.
        """
        try:
            analyze_category, groups = self.flow_service.get_analyze_groups()
            step_name = analyze_category.displayname.lower()
            template_specification = dict(mainContent="header_menu",
                                          section_name=step_name,
                                          controlPage=None,
                                          title="Select an analyzer",
                                          displayControl=False)
            adapters_list = []
            for adapter_group in groups:

                if len(adapter_group.children) > 1:
                    ids = [str(child.id) for child in adapter_group.children]
                    ids = ','.join(ids)
                    adapter_link = '/flow/show_group_of_algorithms/' + str(
                        analyze_category.id) + "/" + ids
                else:
                    adapter_link = self.get_url_adapter(
                        analyze_category.id, adapter_group.children[0].id)

                adapters_list.append({
                    common.KEY_TITLE:
                    adapter_group.name,
                    'link':
                    adapter_link,
                    'description':
                    adapter_group.description,
                    'subsection':
                    adapter_group.children[0].subsection_name
                })
            self.analyze_adapters = adapters_list
            template_specification[common.KEY_SUBMENU_LIST] = adapters_list
            return self.fill_default_attributes(template_specification)

        except ValueError:
            message = 'Could not load analyzers!'
            common.set_warning_message(message)
            self.logger.warning(message)
            raise cherrypy.HTTPRedirect('/tvb')

    @expose_page
    @settings
    @context_selected
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu",
                                      section_name='connectivity',
                                      controlPage=None,
                                      title="Select an algorithm",
                                      displayControl=False,
                                      subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)

    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            ## This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link

    @expose_page
    @settings
    @context_selected
    def show_group_of_algorithms(self, step_key, algorithm_ids):

        project = common.get_current_project()
        category = self.flow_service.get_category_by_id(step_key)
        algorithms = []
        for i in algorithm_ids.split(','):
            algorithm_id = int(i)
            algorithm = self.flow_service.get_algorithm_by_identifier(
                algorithm_id)
            algorithm.link = self.get_url_adapter(step_key, algorithm_id)
            algorithm.input_tree = self.flow_service.prepare_adapter(
                project.id, algorithm)
            algorithms.append(algorithm)

        template_specification = dict(
            mainContent="flow/algorithms_list",
            algorithms=algorithms,
            title="Select an algorithm",
            section_name=category.displayname.lower())
        self._populate_section(algorithms[0], template_specification)
        self.fill_default_attributes(template_specification,
                                     algorithms[0].group_name)
        return template_specification

    @expose_page
    @settings
    @context_selected
    def prepare_group_launch(self, group_gid, step_key, algorithm_id, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data.pop('range_param_name')
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join(dt.gid for dt in datatypes)
        OperationService().group_operation_launch(
            common.get_logged_user().id,
            common.get_current_project().id, int(algorithm_id), int(step_key),
            **data)
        redirect_url = self._compute_back_link('operations',
                                               common.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)

    @expose_page
    @settings
    @context_selected
    def default(self,
                step_key,
                adapter_key,
                cancel=False,
                back_page=None,
                not_reset=False,
                **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = common.get_current_project()
        algorithm = self.flow_service.get_algorithm_by_identifier(adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algorithm is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        is_burst = back_page not in ['operations', 'data']
        if cherrypy.request.method == 'POST':
            data[common.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link,
                                                       step_key, algorithm,
                                                       **data)
            self._populate_section(algorithm, template_specification, is_burst)
        else:
            if (('Referer' not in cherrypy.request.headers or
                 ('Referer' in cherrypy.request.headers
                  and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algorithm.algorithm_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                # might be enforced by MPLH5 on FF.
                not_reset = True
            template_specification = self.get_template_for_adapter(
                project.id,
                step_key,
                algorithm,
                submit_link,
                not not_reset,
                is_burst=is_burst)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if common.KEY_SUBMIT_LINK not in template_specification:
            template_specification[common.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[common.KEY_DISPLAY_MENU] = False
        else:
            template_specification[common.KEY_DISPLAY_MENU] = True
            template_specification[common.KEY_BACK_PAGE] = back_page_link

        template_specification[common.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification,
                                     algorithm.displayname)
        return template_specification

    @expose_fragment("flow/reduce_dimension_select")
    def gettemplatefordimensionselect(self,
                                      entity_gid=None,
                                      select_name="",
                                      reset_session='False',
                                      parameters_prefix="dimensions",
                                      required_dimension=1,
                                      expected_shape="",
                                      operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid 
            the GID of the entity for which is displayed the component
        
        select_name
            the name of the parent select. The select in which
            is displayed the entity with the given GID
  
        parameters_prefix 
            a string which will be used for computing the names of the component

        required_dimension
            the expected dimension for the resulted array

        expected_shape and operations
            used for applying conditions on the resulted array
            e.g.: If the resulted array is a 3D array and we want that the length of the second
            dimension to be smaller then 512 then the expected_shape and operations should be:
            ``expected_shape=x,512,x`` and ``operations='x,<,x``
        """
        template_params = {
            "select_name": "",
            "data": [],
            "parameters_prefix": parameters_prefix,
            "array_shape": "",
            "required_dimension": required_dimension,
            "currentDim": "",
            "required_dim_msg": "",
            "expected_shape": expected_shape,
            "operations": operations
        }

        #if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not string2bool(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items()
                                if k.startswith(starts_with_str))
            dimensions, selected_agg_functions, required_dimension, _ = MappedArray(
            ).parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(
                    list(array_shape), dimensions, selected_agg_functions)
                if required_dimension is not None and current_dim != int(
                        required_dimension):
                    template_params[
                        "required_dim_msg"] = "Please select a " + str(
                            required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(
                        current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels'
                           ) and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    #make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions')
                        and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions)
                        == len(array_shape)):
                    #will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = {}
                            for function_key in function:
                                func_dict[
                                    function_key] = default_agg_functions[
                                        function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in xrange(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params

    @staticmethod
    def _compute_current_dimension(array_shape, selected_items,
                                   selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in xrange(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim

    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}

    @expose_fragment("flow/type2component/datatype2select_simple")
    def getfiltereddatatypes(self, name, parent_div, tree_session_key,
                             filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            common.set_error_message(
                "Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(
            FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            #Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][
                    FILTER_TYPE] == 'date':
                try:
                    temp_date = string2date(filters[FILTER_VALUES][i], False)
                    filters[FILTER_VALUES][i] = temp_date
                except ValueError:
                    raise
        #In order for the filter object not to "stack up" on multiple calls to
        #this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        #Get dataTypes that match the filters from DB then populate with values
        values, total_count = InputTreeManager(
        ).populate_option_values_for_dtype(common.get_current_project().id,
                                           datatype, new_filter,
                                           self.context.get_current_step())
        #Create a dictionary that matches what the template expects
        parameters = {
            ABCAdapter.KEY_NAME: name,
            ABCAdapter.KEY_FILTERABLE: availablefilter,
            ABCAdapter.KEY_TYPE: ABCAdapter.TYPE_SELECT,
            ABCAdapter.KEY_OPTIONS: values,
            ABCAdapter.KEY_DATATYPE: datatype
        }

        if total_count > MAXIMUM_DATA_TYPES_DISPLAYED:
            parameters[KEY_WARNING] = WARNING_OVERFLOW

        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[
                ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and string2bool(
                    str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(
                    values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected in [str(vv['value']) for vv in values]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected

        template_specification = {
            "inputRow": parameters,
            "disabled": False,
            "parentDivId": parent_div,
            common.KEY_SESSION_TREE: tree_session_key
        }
        return self.fill_default_attributes(template_specification)

    def _get_node(self, input_tree, name):
        """
        Given a input tree and a variable name, check to see if any default filters exist.
        """
        for entry in input_tree:
            if (ABCAdapter.KEY_DATATYPE in entry
                    and ABCAdapter.KEY_NAME in entry
                    and str(entry[ABCAdapter.KEY_NAME]) == str(name)):
                return entry
            if entry.get(ABCAdapter.KEY_ATTRIBUTES) is not None:
                in_attr = self._get_node(entry[ABCAdapter.KEY_ATTRIBUTES],
                                         name)
                if in_attr is not None:
                    return in_attr
            if entry.get(ABCAdapter.KEY_OPTIONS) is not None:
                in_options = self._get_node(entry[ABCAdapter.KEY_OPTIONS],
                                            name)
                if in_options is not None:
                    return in_options
        return None

    def execute_post(self, project_id, submit_url, step_key, algorithm,
                     **data):
        """ Execute HTTP POST on a generic step."""
        errors = None
        adapter_instance = ABCAdapter.build_adapter(algorithm)

        try:
            result = self.flow_service.fire_operation(adapter_instance,
                                                      common.get_logged_user(),
                                                      project_id, **data)

            # Store input data in session, for informing user of it.
            step = self.flow_service.get_category_by_id(step_key)
            if not step.rawinput:
                self.context.add_adapter_to_session(None, None,
                                                    copy.deepcopy(data))

            if isinstance(adapter_instance, ABCDisplayer):
                if isinstance(result, dict):
                    result[common.
                           KEY_OPERATION_ID] = adapter_instance.operation_id
                    return result
                else:
                    common.set_error_message(
                        "Invalid result returned from Displayer! Dictionary is expected!"
                    )
            else:
                if isinstance(result, list):
                    result = "Launched %s operations." % len(result)
                common.set_important_message(str(result))
        except formencode.Invalid, excep:
            errors = excep.unpack_errors()
        except OperationException, excep1:
            self.logger.exception("Error while executing a Launch procedure:" +
                                  excep1.message)
            common.set_error_message(excep1.message)
Esempio n. 4
0
class FlowController(base.BaseController):
    """
    This class takes care of executing steps in projects.
    """
    def __init__(self):
        base.BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()

    @cherrypy.expose
    @using_template('base_template')
    @logged()
    @base.settings()
    @context_selected()
    def step(self, step_key=None):
        """
        Choose exact action/adapter for current step.
        """
        category = self.flow_service.get_category_by_id(step_key)
        if category is None:
            message = 'Inconsistent Step Name! Please excuse the wrong link!'
            base.set_warning_message(message)
            self.logger.warning(message + '- Wrong step:' + str(step_key))
            raise cherrypy.HTTPRedirect('/tvb')

        step_name = category.displayname.lower()
        template_specification = dict(mainContent="header_menu",
                                      section_name=step_name,
                                      controlPage=None,
                                      title="Select an algorithm",
                                      displayControl=False)
        adapters_list = []
        for algo_group in self.flow_service.get_groups_for_categories(
            [category]):
            if algo_group.ui_display < 0:
                continue
            adapter_link = self.get_url_adapter(step_key, algo_group.id)
            adapters_list.append({
                base.KEY_TITLE: algo_group.displayname,
                'link': adapter_link,
                'description': algo_group.description,
                'subsection': algo_group.subsection_name
            })
        self.analyze_adapters = adapters_list
        template_specification[base.KEY_SUBMENU_LIST] = adapters_list
        return self.fill_default_attributes(template_specification)

    @cherrypy.expose
    @using_template('base_template')
    @base.settings()
    @logged()
    @context_selected()
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu",
                                      section_name='connectivity',
                                      controlPage=None,
                                      title="Select an algorithm",
                                      displayControl=False,
                                      subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)

    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            ## This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link

    @cherrypy.expose
    @base.settings()
    @logged()
    @context_selected()
    @using_template('base_template')
    def prepare_group_launch(self, group_gid, step_key, adapter_key, **data):
        """
        Recieves as input a group gid and an algorithm given by category and id, along 
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of gid's for all the datatypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data['range_param_name']
        del data['range_param_name']
        data[PARAM_RANGE_1] = range_param_name
        data[range_param_name] = ','.join([dt.gid for dt in datatypes])
        OperationService().group_operation_launch(
            base.get_logged_user().id,
            base.get_current_project().id, int(adapter_key), int(step_key),
            **data)
        redirect_url = self._compute_back_link('operations',
                                               base.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)

    @cherrypy.expose
    @using_template('base_template')
    @base.settings()
    @logged()
    @context_selected()
    def default(self,
                step_key,
                adapter_key,
                cancel=False,
                back_page=None,
                not_reset=False,
                **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = base.get_current_project()
        algo_group = self.flow_service.get_algo_group_by_identifier(
            adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algo_group is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        if cherrypy.request.method == 'POST':
            back_indicator = back_page if back_page == 'burst' else 'operations'
            success_url = self._compute_back_link(back_indicator, project)
            data[base.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link,
                                                       success_url, step_key,
                                                       algo_group, **data)
        else:
            if (('Referer' not in cherrypy.request.headers or
                 ('Referer' in cherrypy.request.headers
                  and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algo_group.group_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                # might be enforced by MPLH5 on FF.
                not_reset = True
            template_specification = self.get_template_for_adapter(
                project.id, step_key, algo_group, submit_link, not not_reset)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if base.KEY_SUBMIT_LINK not in template_specification:
            template_specification[base.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[base.KEY_DISPLAY_MENU] = False
        else:
            template_specification[base.KEY_DISPLAY_MENU] = True
            template_specification[base.KEY_BACK_PAGE] = back_page_link

        template_specification[base.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification, algo_group)
        if (back_page is not None and back_page in ['operations', 'data']
                and not (base.KEY_SECTION in template_specification
                         and template_specification[base.KEY_SECTION]
                         == 'connectivity')):
            template_specification[base.KEY_SECTION] = 'project'
        return template_specification

    @cherrypy.expose
    @using_template("flow/reduce_dimension_select")
    @logged()
    def gettemplatefordimensionselect(self,
                                      entity_gid=None,
                                      select_name="",
                                      reset_session='False',
                                      parameters_prefix="dimensions",
                                      required_dimension=1,
                                      expected_shape="",
                                      operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid - the GID of the entity for which is displayed the component
        select_name - the name of the parent select. The select in which
                      is displayed the entity with the given GID
        parameters_prefix - a string which will be used for computing the names of the component
        required_dimension - the expected dimension for the resulted array
        expected_shape and operations - used for applying conditions on the resulted array
         e.g.: If the resulted array is a 3D array and we want that the length of the second
         dimension to be smaller then 512 then the expected_shape and operations should be:
         expected_shape='x,512,x' and operations='x,&lt;,x'
        """
        template_params = dict()
        template_params["select_name"] = ""
        template_params["data"] = []
        template_params["parameters_prefix"] = parameters_prefix
        template_params["array_shape"] = ""
        template_params["required_dimension"] = required_dimension
        template_params["currentDim"] = ""
        template_params["required_dim_msg"] = ""
        template_params["expected_shape"] = expected_shape
        template_params["operations"] = operations

        #if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not eval(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items()
                                if k.startswith(starts_with_str))
            dimensions, selected_agg_functions, required_dimension, _ = MappedArray(
            ).parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(
                    list(array_shape), dimensions, selected_agg_functions)
                if required_dimension is not None and current_dim != int(
                        required_dimension):
                    template_params[
                        "required_dim_msg"] = "Please select a " + str(
                            required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(
                        current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels'
                           ) and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    #make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions')
                        and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions)
                        == len(array_shape)):
                    #will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = dict()
                            for function_key in function:
                                func_dict[
                                    function_key] = default_agg_functions[
                                        function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in xrange(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params

    @staticmethod
    def _compute_current_dimension(array_shape, selected_items,
                                   selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in xrange(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim

    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}

    @cherrypy.expose
    @using_template("flow/type2component/datatype2select_simple")
    @logged()
    def getfiltereddatatypes(self, name, parent_div, tree_session_key,
                             filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            base.set_error_message(
                "Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(
            FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            #Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][
                    FILTER_TYPE] == 'date':
                try:
                    filter_ = string2date(filter_, False)
                    filters[FILTER_VALUES][i] = filter_
                except ValueError, excep:
                    raise excep
        #In order for the filter object not to "stack up" on multiple calls to
        #this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        #Get dataTypes that match the filters from DB then populate with values
        datatypes = self.flow_service.get_available_datatypes(
            base.get_current_project().id, datatype, new_filter)
        values = self.flow_service.populate_values(
            datatypes, datatype, self.context.get_current_step())
        #Create a dictionary that matches what the template expects
        parameters = dict()
        parameters[ABCAdapter.KEY_NAME] = name
        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[
                ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and eval(
                    str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(
                    values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected is not None and previous_selected in [
                str(vv['value']) for vv in values
        ]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected
        parameters[ABCAdapter.KEY_FILTERABLE] = availablefilter
        parameters[ABCAdapter.KEY_TYPE] = ABCAdapter.TYPE_SELECT
        parameters[ABCAdapter.KEY_OPTIONS] = values
        parameters[ABCAdapter.KEY_DATATYPE] = datatype
        template_specification = {
            "inputRow": parameters,
            "disabled": False,
            "parentDivId": parent_div,
            base.KEY_SESSION_TREE: tree_session_key
        }
        return self.fill_default_attributes(template_specification)
Esempio n. 5
0
class FlowController(BaseController):
    """
    This class takes care of executing steps in projects.
    """
    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()

    @expose_page
    @settings
    @context_selected
    def step_analyzers(self):
        """
        Choose exact action/adapter for current step.
        """
        try:
            analyze_category, groups = self.flow_service.get_analyze_groups()
            step_name = analyze_category.displayname.lower()
            template_specification = dict(mainContent="header_menu",
                                          section_name=step_name,
                                          controlPage=None,
                                          title="Select an analyzer",
                                          displayControl=False)
            adapters_list = []
            for adapter_group in groups:

                if len(adapter_group.children) > 1:
                    ids = [str(child.id) for child in adapter_group.children]
                    ids = ','.join(ids)
                    adapter_link = '/flow/show_group_of_algorithms/' + str(
                        analyze_category.id) + "/" + ids
                else:
                    adapter_link = self.get_url_adapter(
                        analyze_category.id, adapter_group.children[0].id)

                adapters_list.append({
                    common.KEY_TITLE:
                    adapter_group.name,
                    'link':
                    adapter_link,
                    'description':
                    adapter_group.description,
                    'subsection':
                    adapter_group.children[0].subsection_name
                })
            self.analyze_adapters = adapters_list
            template_specification[common.KEY_SUBMENU_LIST] = adapters_list
            return self.fill_default_attributes(template_specification)

        except ValueError:
            message = 'Could not load analyzers!'
            common.set_warning_message(message)
            self.logger.warning(message)
            raise cherrypy.HTTPRedirect('/tvb')

    @expose_page
    @settings
    @context_selected
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu",
                                      section_name='connectivity',
                                      controlPage=None,
                                      title="Select an algorithm",
                                      displayControl=False,
                                      subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)

    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            # This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link

    @expose_page
    @settings
    @context_selected
    def show_group_of_algorithms(self, step_key, algorithm_ids):

        project = common.get_current_project()
        category = self.flow_service.get_category_by_id(step_key)
        algorithms = []
        for i in algorithm_ids.split(','):
            algorithm_id = int(i)
            algorithm = self.flow_service.get_algorithm_by_identifier(
                algorithm_id)
            algorithm.link = self.get_url_adapter(step_key, algorithm_id)
            algorithm.input_tree = self.flow_service.prepare_adapter(algorithm)
            algorithms.append(algorithm)

        template_specification = dict(
            mainContent="flow/algorithms_list",
            algorithms=algorithms,
            title="Select an algorithm",
            section_name=category.displayname.lower())
        self._populate_section(algorithms[0], template_specification)
        self.fill_default_attributes(template_specification,
                                     algorithms[0].group_name)
        return template_specification

    @expose_page
    @settings
    @context_selected
    def prepare_group_launch(self, group_gid, step_key, algorithm_id, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data.pop('range_param_name')
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join(dt.gid for dt in datatypes)
        OperationService().group_operation_launch(
            common.get_logged_user().id,
            common.get_current_project().id, int(algorithm_id), int(step_key),
            **data)
        redirect_url = self._compute_back_link('operations',
                                               common.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)

    @expose_page
    @settings
    @context_selected
    def default(self,
                step_key,
                adapter_key,
                cancel=False,
                back_page=None,
                not_reset=False,
                **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = common.get_current_project()
        algorithm = self.flow_service.get_algorithm_by_identifier(adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algorithm is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        is_burst = back_page not in ['operations', 'data']
        if cherrypy.request.method == 'POST':
            data[common.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link,
                                                       step_key, algorithm,
                                                       **data)
            self._populate_section(algorithm, template_specification, is_burst)
        else:
            if (('Referer' not in cherrypy.request.headers or
                 ('Referer' in cherrypy.request.headers
                  and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algorithm.algorithm_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                not_reset = True
            template_specification = self.get_template_for_adapter(
                project.id,
                step_key,
                algorithm,
                submit_link,
                not not_reset,
                is_burst=is_burst)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if common.KEY_SUBMIT_LINK not in template_specification:
            template_specification[common.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[common.KEY_DISPLAY_MENU] = False
        else:
            template_specification[common.KEY_DISPLAY_MENU] = True
            template_specification[common.KEY_BACK_PAGE] = back_page_link

        template_specification[common.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification,
                                     algorithm.displayname)
        return template_specification

    @expose_fragment("flow/reduce_dimension_select")
    def gettemplatefordimensionselect(self,
                                      entity_gid=None,
                                      select_name="",
                                      reset_session='False',
                                      parameters_prefix="dimensions",
                                      required_dimension=1,
                                      expected_shape="",
                                      operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid 
            the GID of the entity for which is displayed the component
        
        select_name
            the name of the parent select. The select in which
            is displayed the entity with the given GID
  
        parameters_prefix 
            a string which will be used for computing the names of the component

        required_dimension
            the expected dimension for the resulted array

        expected_shape and operations
            used for applying conditions on the resulted array
            e.g.: If the resulted array is a 3D array and we want that the length of the second
            dimension to be smaller then 512 then the expected_shape and operations should be:
            ``expected_shape=x,512,x`` and ``operations='x,&lt;,x``
        """
        template_params = {
            "select_name": "",
            "data": [],
            "parameters_prefix": parameters_prefix,
            "array_shape": "",
            "required_dimension": required_dimension,
            "currentDim": "",
            "required_dim_msg": "",
            "expected_shape": expected_shape,
            "operations": operations
        }

        # if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not string2bool(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items()
                                if k.startswith(starts_with_str))
            from tvb.datatypes.arrays import MappedArray

            dimensions, selected_agg_functions, required_dimension, _ = MappedArray(
            ).parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(
                    list(array_shape), dimensions, selected_agg_functions)
                if required_dimension is not None and current_dim != int(
                        required_dimension):
                    template_params[
                        "required_dim_msg"] = "Please select a " + str(
                            required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(
                        current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels'
                           ) and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    # make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions')
                        and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions)
                        == len(array_shape)):
                    # will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = {}
                            for function_key in function:
                                func_dict[
                                    function_key] = default_agg_functions[
                                        function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in range(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params

    @staticmethod
    def _compute_current_dimension(array_shape, selected_items,
                                   selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in range(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim

    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}

    @expose_fragment("flow/type2component/datatype2select_simple")
    def getfiltereddatatypes(self, name, parent_div, tree_session_key,
                             filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            common.set_error_message(
                "Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(
            FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            # Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][
                    FILTER_TYPE] == 'date':
                try:
                    temp_date = string2date(filters[FILTER_VALUES][i], False)
                    filters[FILTER_VALUES][i] = temp_date
                except ValueError:
                    raise
        # In order for the filter object not to "stack up" on multiple calls to
        # this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        # Get dataTypes that match the filters from DB then populate with values
        values, total_count = InputTreeManager(
        ).populate_option_values_for_dtype(common.get_current_project().id,
                                           datatype, new_filter,
                                           self.context.get_current_step())
        # Create a dictionary that matches what the template expects
        parameters = {
            ABCAdapter.KEY_NAME: name,
            ABCAdapter.KEY_FILTERABLE: availablefilter,
            ABCAdapter.KEY_TYPE: ABCAdapter.TYPE_SELECT,
            ABCAdapter.KEY_OPTIONS: values,
            ABCAdapter.KEY_DATATYPE: datatype
        }

        if total_count > MAXIMUM_DATA_TYPES_DISPLAYED:
            parameters[KEY_WARNING] = WARNING_OVERFLOW

        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[
                ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and string2bool(
                    str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(
                    values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected in [str(vv['value']) for vv in values]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected

        template_specification = {
            "inputRow": parameters,
            "disabled": False,
            "parentDivId": parent_div,
            common.KEY_SESSION_TREE: tree_session_key
        }
        return self.fill_default_attributes(template_specification)

    def _get_node(self, input_tree, name):
        """
        Given a input tree and a variable name, check to see if any default filters exist.
        """
        for entry in input_tree:
            if (ABCAdapter.KEY_DATATYPE in entry
                    and ABCAdapter.KEY_NAME in entry
                    and str(entry[ABCAdapter.KEY_NAME]) == str(name)):
                return entry
            if entry.get(ABCAdapter.KEY_ATTRIBUTES) is not None:
                in_attr = self._get_node(entry[ABCAdapter.KEY_ATTRIBUTES],
                                         name)
                if in_attr is not None:
                    return in_attr
            if entry.get(ABCAdapter.KEY_OPTIONS) is not None:
                in_options = self._get_node(entry[ABCAdapter.KEY_OPTIONS],
                                            name)
                if in_options is not None:
                    return in_options
        return None

    def execute_post(self, project_id, submit_url, step_key, algorithm,
                     **data):
        """ Execute HTTP POST on a generic step."""
        errors = None
        adapter_instance = ABCAdapter.build_adapter(algorithm)

        try:
            form = adapter_instance.get_form()(project_id=project_id)
            form.fill_from_post(data)
            dt_dict = None
            if form.validate():
                dt_dict = form.get_dict()
            if dt_dict is None:
                raise formencode.Invalid(
                    "Could not build a dict out of this form!", {},
                    None,
                    error_dict=form.get_errors_dict())
            adapter_instance.submit_form(form)
            result = self.flow_service.fire_operation(adapter_instance,
                                                      common.get_logged_user(),
                                                      project_id, **dt_dict)

            # Store input data in session, for informing user of it.
            step = self.flow_service.get_category_by_id(step_key)
            if not step.rawinput:
                self.context.add_adapter_to_session(None, None,
                                                    copy.deepcopy(data))

            if isinstance(adapter_instance, ABCDisplayer):
                if isinstance(result, dict):
                    result[common.
                           KEY_OPERATION_ID] = adapter_instance.operation_id
                    return result
                else:
                    common.set_error_message(
                        "Invalid result returned from Displayer! Dictionary is expected!"
                    )
            else:
                if isinstance(result, list):
                    result = "Launched %s operations." % len(result)
                common.set_important_message(str(result))
        except formencode.Invalid as excep:
            errors = excep.unpack_errors()
            common.set_error_message("Invalid form inputs")
            self.logger.warning("Invalid form inputs %s" % errors)
        except OperationException as excep1:
            self.logger.exception("Error while executing a Launch procedure:" +
                                  excep1.message)
            common.set_error_message(excep1.message)

        previous_step = self.context.get_current_substep()
        should_reset = previous_step is None or data.get(
            common.KEY_ADAPTER) != previous_step
        template_specification = self.get_template_for_adapter(
            project_id, step_key, algorithm, submit_url, should_reset)
        if (errors is not None) and (template_specification is not None):
            template_specification[common.KEY_ERRORS] = errors
        template_specification[
            common.KEY_OPERATION_ID] = adapter_instance.operation_id
        return template_specification

    def get_template_for_adapter(self,
                                 project_id,
                                 step_key,
                                 stored_adapter,
                                 submit_url,
                                 session_reset=True,
                                 is_burst=True):
        """ Get Input HTML Interface template or a given adapter """
        try:
            if session_reset:
                self.context.clean_from_session()

            group = None
            # Cache some values in session, for performance
            previous_tree = self.context.get_current_input_tree()
            previous_sub_step = self.context.get_current_substep()

            category = self.flow_service.get_category_by_id(step_key)
            title = "Fill parameters for step " + category.displayname.lower()
            if group:
                title = title + " - " + group.displayname

            adapter_instance = self.flow_service.prepare_adapter(
                stored_adapter)

            if adapter_instance.get_input_tree() is None:
                adapter_form = self.flow_service.prepare_adapter_form(
                    adapter_instance, project_id)
                template_specification = dict(submitLink=submit_url,
                                              form=adapter_form,
                                              title=title)

            # TODO: to be removed when all forms are migrated
            else:
                if not session_reset and previous_tree is not None and previous_sub_step == stored_adapter.id:
                    adapter_interface = previous_tree
                else:
                    adapter_interface = self.flow_service.prepare_adapter_tree_interface(
                        adapter_instance, project_id,
                        stored_adapter.fk_category)
                    self.context.add_adapter_to_session(
                        stored_adapter, adapter_interface)

                current_defaults = self.context.get_current_default()
                if current_defaults is not None:
                    # Change default values in tree, according to selected input
                    adapter_interface = InputTreeManager.fill_defaults(
                        adapter_interface, current_defaults)

                template_specification = dict(submitLink=submit_url,
                                              inputList=adapter_interface,
                                              title=title)
            self._populate_section(stored_adapter, template_specification,
                                   is_burst)
            return template_specification
        except OperationException as oexc:
            self.logger.error("Inconsistent Adapter")
            self.logger.exception(oexc)
            common.set_warning_message(
                'Inconsistent Adapter!  Please review the link (development problem)!'
            )
        return None

    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def readserverstaticfile(self, coded_path):
        """
        Retrieve file from Local storage, having a File System Path.
        """
        try:
            with open(url2path(coded_path), "rb") as f:
                return f.read()
        except Exception as excep:
            self.logger.error("Could not retrieve file from path:" +
                              str(coded_path))
            self.logger.exception(excep)

    def _read_datatype_attribute(self,
                                 entity_gid,
                                 dataset_name,
                                 datatype_kwargs='null',
                                 **kwargs):

        self.logger.debug("Starting to read HDF5: " + entity_gid + "/" +
                          dataset_name + "/" + str(kwargs))
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        entity_dt = h5.load_from_index(entity)

        datatype_kwargs = json.loads(datatype_kwargs)
        if datatype_kwargs:
            for key, value in six.iteritems(datatype_kwargs):
                kwargs[key] = ABCAdapter.load_entity_by_gid(value)

        result = getattr(entity_dt, dataset_name)
        if callable(result):
            if kwargs:
                result = result(**kwargs)
            else:
                result = result()
        return result

    @expose_json
    def invoke_adapter(self, algo_id, method_name, entity_gid, **kwargs):
        algorithm = self.flow_service.get_algorithm_by_identifier(algo_id)
        adapter_instance = ABCAdapter.build_adapter(algorithm)
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        storage_path = self.files_helper.get_project_folder(
            entity.parent_operation.project, str(entity.fk_from_operation))
        adapter_instance.storage_path = storage_path
        method = getattr(adapter_instance, method_name)
        if kwargs:
            return method(entity_gid, **kwargs)
        return method(entity_gid)

    @expose_json
    def read_from_h5_file(self,
                          entity_gid,
                          method_name,
                          flatten=False,
                          datatype_kwargs='null',
                          **kwargs):
        self.logger.debug("Starting to read HDF5: " + entity_gid + "/" +
                          method_name + "/" + str(kwargs))
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        entity_h5 = h5.h5_file_for_index(entity)

        datatype_kwargs = json.loads(datatype_kwargs)
        if datatype_kwargs:
            for key, value in six.iteritems(datatype_kwargs):
                kwargs[key] = ABCAdapter.load_entity_by_gid(value)

        result = getattr(entity_h5, method_name)
        if kwargs:
            result = result(**kwargs)
        else:
            result = result()

        entity_h5.close()
        return self._prepare_result(result, flatten)

    @expose_json
    def read_datatype_attribute(self,
                                entity_gid,
                                dataset_name,
                                flatten=False,
                                datatype_kwargs='null',
                                **kwargs):
        """
        Retrieve from a given DataType a property or a method result.

        :returns: JSON representation of the attribute.
        :param entity_gid: GID for DataType entity
        :param dataset_name: name of the dataType property /method 
        :param flatten: result should be flatten before return (use with WebGL data mainly e.g vertices/triangles)
            Ignored if the attribute is not an ndarray
        :param datatype_kwargs: if passed, will contain a dictionary of type {'name' : 'gid'}, and for each such
            pair, a load_entity will be performed and kwargs will be updated to contain the result
        :param kwargs: extra parameters to be passed when dataset_name is method.

        """
        result = self._read_datatype_attribute(entity_gid, dataset_name,
                                               datatype_kwargs, **kwargs)
        return self._prepare_result(result, flatten)

    def _prepare_result(self, result, flatten):
        if isinstance(result, numpy.ndarray):
            # for ndarrays honor the flatten kwarg and convert to lists as ndarrs are not json-able
            if flatten is True or flatten == "True":
                result = result.flatten()
            return result.tolist()
        else:
            return result

    @expose_numpy_array
    def read_binary_datatype_attribute(self,
                                       entity_gid,
                                       dataset_name,
                                       datatype_kwargs='null',
                                       **kwargs):
        return self._read_datatype_attribute(entity_gid, dataset_name,
                                             datatype_kwargs, **kwargs)

    @expose_fragment("flow/genericAdapterFormFields")
    def get_simple_adapter_interface(self,
                                     algorithm_id,
                                     parent_div='',
                                     is_uploader=False):
        """
        AJAX exposed method. Will return only the interface for a adapter, to
        be used when tabs are needed.
        """
        curent_project = common.get_current_project()
        is_uploader = string2bool(is_uploader)
        template_specification = self.get_adapter_template(
            curent_project.id, algorithm_id, is_uploader)
        template_specification[common.KEY_PARENT_DIV] = parent_div
        return self.fill_default_attributes(template_specification)

    @expose_fragment("flow/full_adapter_interface")
    def getadapterinterface(self, project_id, algorithm_id, back_page=None):
        """
        AJAX exposed method. Will return only a piece of a page, 
        to be integrated as part in another page.
        """
        template_specification = self.get_adapter_template(
            project_id, algorithm_id, False, back_page)
        template_specification["isCallout"] = True
        return self.fill_default_attributes(template_specification)

    def get_adapter_template(self,
                             project_id,
                             algorithm_id,
                             is_upload=False,
                             back_page=None):
        """
        Get the template for an adapter based on the algo group id.
        """
        if not (project_id and int(project_id) and
                (algorithm_id is not None) and int(algorithm_id)):
            return ""

        algorithm = self.flow_service.get_algorithm_by_identifier(algorithm_id)
        if is_upload:
            submit_link = "/project/launchloader/" + str(
                project_id) + "/" + str(algorithm_id)
        else:
            submit_link = self.get_url_adapter(algorithm.fk_category,
                                               algorithm.id, back_page)

        current_step = self.context.get_current_substep()
        if current_step is None or str(current_step) != str(algorithm_id):
            self.context.clean_from_session()
        template_specification = self.get_template_for_adapter(
            project_id, algorithm.fk_category, algorithm, submit_link,
            is_upload)
        if template_specification is None:
            return ""
        template_specification[common.KEY_DISPLAY_MENU] = not is_upload
        return template_specification

    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reloadoperation(self, operation_id, **_):
        """Redirect to Operation Input selection page, 
        with input data already selected."""
        operation = self.flow_service.load_operation(operation_id)
        data = parse_json_parameters(operation.parameters)
        self.context.add_adapter_to_session(operation.algorithm, None, data)
        category_id = operation.algorithm.fk_category
        algo_id = operation.fk_from_algo
        raise cherrypy.HTTPRedirect("/flow/" + str(category_id) + "/" +
                                    str(algo_id) + "?not_reset=True")

    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reload_burst_operation(self, operation_id, is_group, **_):
        """
        Find out from which burst was this operation launched. Set that burst as the selected one and 
        redirect to the burst page.
        """
        is_group = int(is_group)
        if not is_group:
            operation = self.flow_service.load_operation(int(operation_id))
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            first_op = ProjectService.get_operations_in_group(op_group)[0]
            operation = self.flow_service.load_operation(int(first_op.id))
        operation.burst.prepare_after_load()
        common.add2session(common.KEY_BURST_CONFIG, operation.burst)
        raise cherrypy.HTTPRedirect("/burst/")

    @expose_json
    def stop_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.
        """
        operation_service = OperationService()
        result = False
        if int(is_group) == 0:
            result = operation_service.stop_operation(operation_id)
            if remove_after_stop:
                ProjectService().remove_operation(operation_id)
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            operations_in_group = ProjectService.get_operations_in_group(
                op_group)
            for operation in operations_in_group:
                tmp_res = operation_service.stop_operation(operation.id)
                if remove_after_stop:
                    ProjectService().remove_operation(operation.id)
                result = result or tmp_res
        return result

    @expose_json
    def stop_burst_operation(self,
                             operation_id,
                             is_group,
                             remove_after_stop=False):
        """
        For a given operation id that is part of a burst just stop the given burst.
        :returns True when stopped operation was successfully.
        """
        operation_id = int(operation_id)
        if int(is_group) == 0:
            operation = self.flow_service.load_operation(operation_id)
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            first_op = ProjectService.get_operations_in_group(op_group)[0]
            operation = self.flow_service.load_operation(int(first_op.id))

        try:
            burst_service = BurstService()
            result = burst_service.stop_burst(operation.burst)
            if remove_after_stop:
                current_burst = common.get_from_session(
                    common.KEY_BURST_CONFIG)
                if current_burst and current_burst.id == operation.burst.id:
                    common.remove_from_session(common.KEY_BURST_CONFIG)
                result = burst_service.cancel_or_remove_burst(
                    operation.burst.id) or result

            return result
        except Exception as ex:
            self.logger.exception(ex)
            return False

    def fill_default_attributes(self, template_dictionary, title='-'):
        """
        Overwrite base controller to add required parameters for adapter templates.
        """
        if common.KEY_TITLE not in template_dictionary:
            template_dictionary[common.KEY_TITLE] = title

        if common.KEY_PARENT_DIV not in template_dictionary:
            template_dictionary[common.KEY_PARENT_DIV] = ''
        if common.KEY_PARAMETERS_CONFIG not in template_dictionary:
            template_dictionary[common.KEY_PARAMETERS_CONFIG] = False

        template_dictionary[
            common.KEY_INCLUDE_RESOURCES] = 'flow/included_resources'
        BaseController.fill_default_attributes(self, template_dictionary)
        return template_dictionary

    ##### Below this point are operations that might be moved to different #####
    ##### controller                                                       #####

    NEW_SELECTION_NAME = 'New selection'

    def _get_available_selections(self, datatype_gid):
        """
        selection retrieval common to selection component and connectivity selection
        """
        curent_project = common.get_current_project()
        selections = self.flow_service.get_selections_for_project(
            curent_project.id, datatype_gid)
        names, sel_values = [], []

        for selection in selections:
            names.append(selection.ui_name)
            sel_values.append(selection.selected_nodes)

        return names, sel_values

    @expose_fragment('visualizers/commons/channel_selector_opts')
    def get_available_selections(self, **data):
        sel_names, sel_values = self._get_available_selections(
            data['datatype_gid'])
        return dict(namedSelections=list(zip(sel_names, sel_values)))

    @expose_json
    def store_measure_points_selection(self, ui_name, **data):
        """
        Save a MeasurePoints selection (new or update existing entity).
        """
        if ui_name and ui_name != self.NEW_SELECTION_NAME:
            sel_project_id = common.get_current_project().id
            # client sends integers as strings:
            selection = json.dumps(
                [int(s) for s in json.loads(data['selection'])])
            datatype_gid = data['datatype_gid']
            self.flow_service.save_measure_points_selection(
                ui_name, selection, datatype_gid, sel_project_id)
            return [True, 'Selection saved successfully.']

        else:
            error_msg = self.NEW_SELECTION_NAME + " or empty name are not  valid as selection names."
            return [False, error_msg]

    @expose_fragment(
        "visualizers/pse_discrete/inserting_new_threshold_spec_bar")
    def create_row_of_specs(self, count):
        return dict(id_increment_count=count)

    @expose_json
    def store_pse_filter(self, config_name, **data):
        # this will need to be updated in such a way that the expose_json actually gets used
        ## also this is going to be changed to be storing through the flow service and dao. Stay updated
        try:
            ##this is to check whether there is already an entry in the
            for i, (name, Val) in enumerate(self.PSE_names_list):
                if name == config_name:
                    self.PSE_names_list[i] = (
                        config_name,
                        (data['threshold_value'] + "," +
                         data['threshold_type'] + "," + data['not_presence'])
                    )  # replace the previous occurence of the config name, and carry on.
                    self.get_pse_filters()
                    return [
                        True, 'Selected Text stored, and selection updated'
                    ]
            self.PSE_names_list.append(
                (config_name,
                 (data['threshold_value'] + "," + data['threshold_type'] +
                  "," + data['not_presence'])))
        except AttributeError:
            self.PSE_names_list = [
                (config_name,
                 (data['threshold_value'] + "," + data['threshold_type'] +
                  "," + data['not_presence']))
            ]

        self.get_pse_filters()
        return [True, 'Selected Text stored, and selection updated']

    @expose_fragment("visualizers/commons/channel_selector_opts")
    def get_pse_filters(self):
        try:
            return dict(namedSelections=self.PSE_names_list)
        except AttributeError:
            return dict(
                namedSelections=[]
            )  # this will give us back atleast the New Selection option in the select
        except:
            raise

    @expose_json
    def store_exploration_section(self, val_range, step, dt_group_guid):
        """
        Launching method for further simulations.
        """
        range_list = [float(num) for num in val_range.split(",")]
        step_list = [float(num) for num in step.split(",")]

        datatype_group_ob = ProjectService().get_datatypegroup_by_gid(
            dt_group_guid)
        operation_grp = datatype_group_ob.parent_operation_group
        operation_obj = self.flow_service.load_operation(
            datatype_group_ob.fk_from_operation)
        parameters = json.loads(operation_obj.parameters)

        range1name, range1_dict = json.loads(operation_grp.range1)
        range2name, range2_dict = json.loads(operation_grp.range2)
        parameters[RANGE_PARAMETER_1] = range1name
        parameters[RANGE_PARAMETER_2] = range2name

        ##change the existing simulator parameters to be min max step types
        range1_dict = {
            constants.ATT_MINVALUE: range_list[0],
            constants.ATT_MAXVALUE: range_list[1],
            constants.ATT_STEP: step_list[0]
        }
        range2_dict = {
            constants.ATT_MINVALUE: range_list[2],
            constants.ATT_MAXVALUE: range_list[3],
            constants.ATT_STEP: step_list[1]
        }
        parameters[range1name] = json.dumps(
            range1_dict)  # this is for the x axis parameter
        parameters[range2name] = json.dumps(
            range2_dict)  # this is for the y axis parameter

        OperationService().group_operation_launch(
            common.get_logged_user().id,
            common.get_current_project().id, operation_obj.algorithm.id,
            operation_obj.algorithm.fk_category, datatype_group_ob,
            **parameters)

        return [True, 'Stored the exploration material successfully']
 def __init__(self):
     BaseController.__init__(self)
     self.context = SelectedAdapterContext()
     self.files_helper = FilesHelper()
class FlowController(BaseController):
    """
    This class takes care of executing steps in projects.
    """

    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()


    @expose_page
    @settings
    @context_selected
    def step_analyzers(self):
        """
        Choose exact action/adapter for current step.
        """
        try:
            analyze_category, groups = self.flow_service.get_analyze_groups()
            step_name = analyze_category.displayname.lower()
            template_specification = dict(mainContent="header_menu", section_name=step_name, controlPage=None,
                                          title="Select an analyzer", displayControl=False)
            adapters_list = []
            for adapter_group in groups:

                if len(adapter_group.children) > 1:
                    ids = [str(child.id) for child in adapter_group.children]
                    ids = ','.join(ids)
                    adapter_link = '/flow/show_group_of_algorithms/' + str(analyze_category.id) + "/" + ids
                else:
                    adapter_link = self.get_url_adapter(analyze_category.id, adapter_group.children[0].id)

                adapters_list.append({common.KEY_TITLE: adapter_group.name,
                                      'link': adapter_link,
                                      'description': adapter_group.description,
                                      'subsection': adapter_group.children[0].subsection_name})
            self.analyze_adapters = adapters_list
            template_specification[common.KEY_SUBMENU_LIST] = adapters_list
            return self.fill_default_attributes(template_specification)

        except ValueError:
            message = 'Could not load analyzers!'
            common.set_warning_message(message)
            self.logger.warning(message)
            raise cherrypy.HTTPRedirect('/tvb')


    @expose_page
    @settings
    @context_selected
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu", section_name='connectivity', controlPage=None,
                                      title="Select an algorithm", displayControl=False, subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)


    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            ## This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link


    @expose_page
    @settings
    @context_selected
    def show_group_of_algorithms(self, step_key, algorithm_ids):

        project = common.get_current_project()
        category = self.flow_service.get_category_by_id(step_key)
        algorithms = []
        for i in algorithm_ids.split(','):
            algorithm_id = int(i)
            algorithm = self.flow_service.get_algorithm_by_identifier(algorithm_id)
            algorithm.link = self.get_url_adapter(step_key, algorithm_id)
            algorithm.input_tree = self.flow_service.prepare_adapter(project.id, algorithm)
            algorithms.append(algorithm)

        template_specification = dict(mainContent="flow/algorithms_list", algorithms=algorithms,
                                      title="Select an algorithm", section_name=category.displayname.lower())
        self._populate_section(algorithms[0], template_specification)
        self.fill_default_attributes(template_specification, algorithms[0].group_name)
        return template_specification


    @expose_page
    @settings
    @context_selected
    def prepare_group_launch(self, group_gid, step_key, algorithm_id, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data.pop('range_param_name')
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join(dt.gid for dt in datatypes)
        OperationService().group_operation_launch(common.get_logged_user().id, common.get_current_project().id,
                                                  int(algorithm_id), int(step_key), **data)
        redirect_url = self._compute_back_link('operations', common.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)
    

    @expose_page
    @settings
    @context_selected
    def default(self, step_key, adapter_key, cancel=False, back_page=None, not_reset=False, **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = common.get_current_project()
        algorithm = self.flow_service.get_algorithm_by_identifier(adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algorithm is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        is_burst = back_page not in ['operations', 'data']
        if cherrypy.request.method == 'POST':
            data[common.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link, step_key, algorithm, **data)
            self._populate_section(algorithm, template_specification, is_burst)
        else:
            if (('Referer' not in cherrypy.request.headers or
                ('Referer' in cherrypy.request.headers and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algorithm.algorithm_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                # might be enforced by MPLH5 on FF.
                not_reset = True
            template_specification = self.get_template_for_adapter(project.id, step_key, algorithm,
                                                                   submit_link, not not_reset, is_burst=is_burst)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if common.KEY_SUBMIT_LINK not in template_specification:
            template_specification[common.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[common.KEY_DISPLAY_MENU] = False
        else:
            template_specification[common.KEY_DISPLAY_MENU] = True
            template_specification[common.KEY_BACK_PAGE] = back_page_link

        template_specification[common.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification, algorithm.displayname)
        return template_specification


    @expose_fragment("flow/reduce_dimension_select")
    def gettemplatefordimensionselect(self, entity_gid=None, select_name="", reset_session='False',
                                      parameters_prefix="dimensions", required_dimension=1,
                                      expected_shape="", operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid 
            the GID of the entity for which is displayed the component
        
        select_name
            the name of the parent select. The select in which
            is displayed the entity with the given GID
  
        parameters_prefix 
            a string which will be used for computing the names of the component

        required_dimension
            the expected dimension for the resulted array

        expected_shape and operations
            used for applying conditions on the resulted array
            e.g.: If the resulted array is a 3D array and we want that the length of the second
            dimension to be smaller then 512 then the expected_shape and operations should be:
            ``expected_shape=x,512,x`` and ``operations='x,&lt;,x``
        """
        template_params = {"select_name": "",
                           "data": [],
                           "parameters_prefix": parameters_prefix,
                           "array_shape": "",
                           "required_dimension": required_dimension,
                           "currentDim": "",
                           "required_dim_msg": "",
                           "expected_shape": expected_shape,
                           "operations": operations}

        #if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not string2bool(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items() if k.startswith(starts_with_str))
            dimensions, selected_agg_functions, required_dimension, _ = MappedArray().parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(list(array_shape), dimensions,
                                                                         selected_agg_functions)
                if required_dimension is not None and current_dim != int(required_dimension):
                    template_params["required_dim_msg"] = "Please select a " + str(required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels') and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    #make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions') and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions) == len(array_shape)):
                    #will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = {}
                            for function_key in function:
                                func_dict[function_key] = default_agg_functions[function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in xrange(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params


    @staticmethod
    def _compute_current_dimension(array_shape, selected_items, selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in xrange(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim


    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}


    @expose_fragment("flow/type2component/datatype2select_simple")
    def getfiltereddatatypes(self, name, parent_div, tree_session_key, filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            common.set_error_message("Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            #Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][FILTER_TYPE] == 'date':
                try:
                    temp_date = string2date(filters[FILTER_VALUES][i], False)
                    filters[FILTER_VALUES][i] = temp_date
                except ValueError:
                    raise
        #In order for the filter object not to "stack up" on multiple calls to
        #this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        #Get dataTypes that match the filters from DB then populate with values
        values, total_count = InputTreeManager().populate_option_values_for_dtype(
                                    common.get_current_project().id,
                                    datatype, new_filter,
                                    self.context.get_current_step() )
        #Create a dictionary that matches what the template expects
        parameters = {ABCAdapter.KEY_NAME: name,
                      ABCAdapter.KEY_FILTERABLE: availablefilter,
                      ABCAdapter.KEY_TYPE: ABCAdapter.TYPE_SELECT,
                      ABCAdapter.KEY_OPTIONS: values,
                      ABCAdapter.KEY_DATATYPE: datatype}

        if total_count > MAXIMUM_DATA_TYPES_DISPLAYED:
            parameters[KEY_WARNING] = WARNING_OVERFLOW

        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and string2bool(str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected in [str(vv['value']) for vv in values]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected

        template_specification = {"inputRow": parameters, "disabled": False,
                                  "parentDivId": parent_div, common.KEY_SESSION_TREE: tree_session_key}
        return self.fill_default_attributes(template_specification)


    def _get_node(self, input_tree, name):
        """
        Given a input tree and a variable name, check to see if any default filters exist.
        """
        for entry in input_tree:
            if (ABCAdapter.KEY_DATATYPE in entry and ABCAdapter.KEY_NAME in entry
                    and str(entry[ABCAdapter.KEY_NAME]) == str(name)):
                return entry
            if entry.get(ABCAdapter.KEY_ATTRIBUTES) is not None:
                in_attr = self._get_node(entry[ABCAdapter.KEY_ATTRIBUTES], name)
                if in_attr is not None:
                    return in_attr
            if entry.get(ABCAdapter.KEY_OPTIONS) is not None:
                in_options = self._get_node(entry[ABCAdapter.KEY_OPTIONS], name)
                if in_options is not None:
                    return in_options
        return None


    def execute_post(self, project_id, submit_url, step_key, algorithm, **data):
        """ Execute HTTP POST on a generic step."""
        errors = None
        adapter_instance = ABCAdapter.build_adapter(algorithm)

        try:
            result = self.flow_service.fire_operation(adapter_instance, common.get_logged_user(), project_id, **data)

            # Store input data in session, for informing user of it.
            step = self.flow_service.get_category_by_id(step_key)
            if not step.rawinput:
                self.context.add_adapter_to_session(None, None, copy.deepcopy(data))

            if isinstance(adapter_instance, ABCDisplayer):
                if isinstance(result, dict):
                    result[common.KEY_OPERATION_ID] = adapter_instance.operation_id
                    return result
                else:
                    common.set_error_message("Invalid result returned from Displayer! Dictionary is expected!")
            else:
                if isinstance(result, list):
                    result = "Launched %s operations." % len(result)
                common.set_important_message(str(result))
        except formencode.Invalid, excep:
            errors = excep.unpack_errors()
        except OperationException, excep1:
            self.logger.exception("Error while executing a Launch procedure:" + excep1.message)
            common.set_error_message(excep1.message)
Esempio n. 8
0
 def __init__(self):
     BaseController.__init__(self)
     self.context = SelectedAdapterContext()
     self.files_helper = FilesHelper()
     self.operation_services = OperationService()
Esempio n. 9
0
class FlowController(BaseController):
    """
    This class takes care of executing steps in projects.
    """
    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()
        self.operation_services = OperationService()

    @expose_page
    @settings
    @context_selected
    def step_analyzers(self):
        """
        Choose exact action/adapter for current step.
        """
        try:
            analyze_category, groups = self.algorithm_service.get_analyze_groups(
            )
            step_name = analyze_category.displayname.lower()
            template_specification = dict(mainContent="header_menu",
                                          section_name=step_name,
                                          controlPage=None,
                                          title="Select an analyzer",
                                          displayControl=False)
            adapters_list = []
            for adapter_group in groups:

                if len(adapter_group.children) > 1:
                    ids = [str(child.id) for child in adapter_group.children]
                    ids = ','.join(ids)
                    adapter_link = '/flow/show_group_of_algorithms/' + str(
                        analyze_category.id) + "/" + ids
                else:
                    adapter_link = self.get_url_adapter(
                        analyze_category.id, adapter_group.children[0].id)

                adapters_list.append({
                    common.KEY_TITLE:
                    adapter_group.name,
                    'link':
                    adapter_link,
                    'description':
                    adapter_group.description,
                    'subsection':
                    adapter_group.children[0].subsection_name
                })
            self.analyze_adapters = adapters_list
            template_specification[common.KEY_SUBMENU_LIST] = adapters_list
            return self.fill_default_attributes(template_specification)

        except ValueError:
            message = 'Could not load analyzers!'
            common.set_warning_message(message)
            self.logger.warning(message)
            raise cherrypy.HTTPRedirect('/tvb')

    @expose_page
    @settings
    @context_selected
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu",
                                      section_name='connectivity',
                                      controlPage=None,
                                      title="Select an algorithm",
                                      displayControl=False,
                                      subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)

    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            # This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link

    @expose_page
    @settings
    @context_selected
    def show_group_of_algorithms(self, step_key, algorithm_ids):

        project = common.get_current_project()
        category = self.algorithm_service.get_category_by_id(step_key)
        algorithms = []
        for i in algorithm_ids.split(','):
            algorithm_id = int(i)
            algorithm = self.algorithm_service.get_algorithm_by_identifier(
                algorithm_id)
            algorithm.link = self.get_url_adapter(step_key, algorithm_id)
            adapter_instance = self.algorithm_service.prepare_adapter(
                algorithm)
            adapter_form = self.algorithm_service.prepare_adapter_form(
                adapter_instance, project.id)
            algorithm.form = self.render_adapter_form(adapter_form)
            algorithms.append(algorithm)

        template_specification = dict(
            mainContent="flow/algorithms_list",
            algorithms=algorithms,
            title="Select an algorithm",
            section_name=category.displayname.lower())
        self._populate_section(algorithms[0], template_specification)
        self.fill_default_attributes(template_specification,
                                     algorithms[0].group_name)
        return template_specification

    @expose_page
    @settings
    @context_selected
    def prepare_group_launch(self, group_gid, step_key, algorithm_id, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data.pop('range_param_name')
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join(dt.gid for dt in datatypes)
        self.operation_services.group_operation_launch(
            common.get_logged_user().id, common.get_current_project(),
            int(algorithm_id), int(step_key), **data)
        redirect_url = self._compute_back_link('operations',
                                               common.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)

    @expose_page
    @settings
    @context_selected
    def default(self,
                step_key,
                adapter_key,
                cancel=False,
                back_page=None,
                not_reset=False,
                **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = common.get_current_project()
        algorithm = self.algorithm_service.get_algorithm_by_identifier(
            adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algorithm is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        is_burst = back_page not in ['operations', 'data']
        if cherrypy.request.method == 'POST':
            data[common.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link,
                                                       step_key, algorithm,
                                                       **data)
            self._populate_section(algorithm, template_specification, is_burst)
        else:
            if (('Referer' not in cherrypy.request.headers or
                 ('Referer' in cherrypy.request.headers
                  and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algorithm.algorithm_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                not_reset = True
            template_specification = self.get_template_for_adapter(
                project.id,
                step_key,
                algorithm,
                submit_link,
                not not_reset,
                is_burst=is_burst)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if common.KEY_SUBMIT_LINK not in template_specification:
            template_specification[common.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[common.KEY_DISPLAY_MENU] = False
        else:
            template_specification[common.KEY_DISPLAY_MENU] = True
            template_specification[common.KEY_BACK_PAGE] = back_page_link

        template_specification[common.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification,
                                     algorithm.displayname)
        return template_specification

    @expose_fragment('form_fields/options_field')
    @settings
    @context_selected
    def get_filtered_datatypes(self, dt_module, dt_class, filters,
                               has_all_option, has_none_option):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        index_class = getattr(sys.modules[dt_module], dt_class)()
        filters_dict = json.loads(filters)

        fields = []
        operations = []
        values = []

        for idx in range(len(filters_dict['fields'])):
            fields.append(filters_dict['fields'][idx])
            operations.append(filters_dict['operations'][idx])
            values.append(filters_dict['values'][idx])

        filter = FilterChain(fields=fields,
                             operations=operations,
                             values=values)
        project = common.get_current_project()

        form = Form(project_id=project.id, draw_ranges=True)
        data_type_gid_attr = DataTypeGidAttr(
            linked_datatype=REGISTRY.get_datatype_for_index(index_class))
        data_type_gid_attr.required = not string2bool(has_none_option)

        select_field = TraitDataTypeSelectField(
            data_type_gid_attr,
            form,
            conditions=filter,
            has_all_option=string2bool(has_all_option))

        return {'options': select_field.options()}

    def execute_post(self, project_id, submit_url, step_key, algorithm,
                     **data):
        """ Execute HTTP POST on a generic step."""
        errors = None
        adapter_instance = ABCAdapter.build_adapter(algorithm)

        try:
            form = adapter_instance.get_form()(project_id=project_id)
            if 'fill_defaults' in data:
                form.fill_from_post_plus_defaults(data)
            else:
                form.fill_from_post(data)
            view_model = None
            if form.validate():
                try:
                    view_model = form.get_view_model()()
                    form.fill_trait(view_model)
                except NotImplementedError:
                    self.logger.exception(
                        "Form and/or ViewModel not fully implemented for " +
                        str(form))
                    raise InvalidFormValues(
                        "Invalid form inputs! Could not find a model for this form!",
                        error_dict=form.get_errors_dict())
            else:
                raise InvalidFormValues(
                    "Invalid form inputs! Could not fill algorithm from the given inputs!",
                    error_dict=form.get_errors_dict())

            adapter_instance.submit_form(form)

            if issubclass(type(adapter_instance), ABCDisplayer):
                adapter_instance.current_project_id = project_id
                adapter_instance.user_id = common.get_logged_user().id
                result = adapter_instance.launch(view_model)
                if isinstance(result, dict):
                    return result
                else:
                    common.set_error_message(
                        "Invalid result returned from Displayer! Dictionary is expected!"
                    )
                return {}

            result = self.operation_services.fire_operation(
                adapter_instance,
                common.get_logged_user(),
                project_id,
                view_model=view_model)
            # Store input data in session, for informing user of it.
            step = self.algorithm_service.get_category_by_id(step_key)
            if not step.rawinput:
                self.context.add_adapter_to_session(None, None,
                                                    copy.deepcopy(data))
            if isinstance(result, list):
                result = "Launched %s operations." % len(result)
            common.set_important_message(str(result))

        except formencode.Invalid as excep:
            errors = excep.unpack_errors()
            common.set_error_message("Invalid form inputs")
            self.logger.warning("Invalid form inputs %s" % errors)
        except (OperationException, LaunchException,
                TraitValueError) as excep1:
            self.logger.exception("Error while executing a Launch procedure:" +
                                  excep1.message)
            common.set_error_message(excep1.message)
        except InvalidFormValues as excep2:
            message, errors = excep2.display_full_errors()
            common.set_error_message(message)
            self.logger.warning("%s \n %s" % (message, errors))

        previous_step = self.context.get_current_substep()
        should_reset = previous_step is None or data.get(
            common.KEY_ADAPTER) != previous_step
        template_specification = self.get_template_for_adapter(
            project_id, step_key, algorithm, submit_url, should_reset)
        if (errors is not None) and (template_specification is not None):
            template_specification[common.KEY_ERRORS] = errors
        template_specification[
            common.KEY_OPERATION_ID] = adapter_instance.operation_id
        return template_specification

    def get_template_for_adapter(self,
                                 project_id,
                                 step_key,
                                 stored_adapter,
                                 submit_url,
                                 session_reset=True,
                                 is_burst=True):
        """ Get Input HTML Interface template or a given adapter """
        try:
            if session_reset:
                self.context.clean_from_session()

            group = None
            category = self.algorithm_service.get_category_by_id(step_key)
            title = "Fill parameters for step " + category.displayname.lower()
            if group:
                title = title + " - " + group.displayname

            adapter_instance = self.algorithm_service.prepare_adapter(
                stored_adapter)

            adapter_form = self.algorithm_service.prepare_adapter_form(
                adapter_instance, project_id)
            template_specification = dict(
                submitLink=submit_url,
                adapter_form=self.render_adapter_form(adapter_form),
                title=title)

            self._populate_section(stored_adapter, template_specification,
                                   is_burst)
            return template_specification
        except OperationException as oexc:
            self.logger.error("Inconsistent Adapter")
            self.logger.exception(oexc)
            common.set_warning_message(
                'Inconsistent Adapter!  Please review the link (development problem)!'
            )
        return None

    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def readserverstaticfile(self, coded_path):
        """
        Retrieve file from Local storage, having a File System Path.
        """
        try:
            with open(url2path(coded_path), "rb") as f:
                return f.read()
        except Exception as excep:
            self.logger.error("Could not retrieve file from path:" +
                              str(coded_path))
            self.logger.exception(excep)

    def _read_datatype_attribute(self,
                                 entity_gid,
                                 dataset_name,
                                 datatype_kwargs='null',
                                 **kwargs):

        self.logger.debug("Starting to read HDF5: " + entity_gid + "/" +
                          dataset_name + "/" + str(kwargs))
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        entity_dt = h5.load_from_index(entity)

        datatype_kwargs = json.loads(datatype_kwargs)
        if datatype_kwargs:
            for key, value in six.iteritems(datatype_kwargs):
                kwargs[key] = ABCAdapter.load_entity_by_gid(value)

        result = getattr(entity_dt, dataset_name)
        if callable(result):
            if kwargs:
                result = result(**kwargs)
            else:
                result = result()
        return result

    @expose_json
    def invoke_adapter(self, algo_id, method_name, entity_gid, **kwargs):
        algorithm = self.algorithm_service.get_algorithm_by_identifier(algo_id)
        adapter_instance = ABCAdapter.build_adapter(algorithm)
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        storage_path = self.files_helper.get_project_folder(
            entity.parent_operation.project, str(entity.fk_from_operation))
        adapter_instance.storage_path = storage_path
        method = getattr(adapter_instance, method_name)
        if kwargs:
            return method(entity_gid, **kwargs)
        return method(entity_gid)

    @expose_json
    def read_from_h5_file(self,
                          entity_gid,
                          method_name,
                          flatten=False,
                          datatype_kwargs='null',
                          **kwargs):
        self.logger.debug("Starting to read HDF5: " + entity_gid + "/" +
                          method_name + "/" + str(kwargs))
        entity = ABCAdapter.load_entity_by_gid(entity_gid)
        entity_h5 = h5.h5_file_for_index(entity)

        datatype_kwargs = json.loads(datatype_kwargs)
        if datatype_kwargs:
            for key, value in six.iteritems(datatype_kwargs):
                kwargs[key] = ABCAdapter.load_entity_by_gid(value)

        result = getattr(entity_h5, method_name)
        if kwargs:
            result = result(**kwargs)
        else:
            result = result()

        entity_h5.close()
        return self._prepare_result(result, flatten)

    @expose_json
    def read_datatype_attribute(self,
                                entity_gid,
                                dataset_name,
                                flatten=False,
                                datatype_kwargs='null',
                                **kwargs):
        """
        Retrieve from a given DataType a property or a method result.

        :returns: JSON representation of the attribute.
        :param entity_gid: GID for DataType entity
        :param dataset_name: name of the dataType property /method
        :param flatten: result should be flatten before return (use with WebGL data mainly e.g vertices/triangles)
            Ignored if the attribute is not an ndarray
        :param datatype_kwargs: if passed, will contain a dictionary of type {'name' : 'gid'}, and for each such
            pair, a load_entity will be performed and kwargs will be updated to contain the result
        :param kwargs: extra parameters to be passed when dataset_name is method.

        """
        result = self._read_datatype_attribute(entity_gid, dataset_name,
                                               datatype_kwargs, **kwargs)
        return self._prepare_result(result, flatten)

    def _prepare_result(self, result, flatten):
        if isinstance(result, numpy.ndarray):
            # for ndarrays honor the flatten kwarg and convert to lists as ndarrs are not json-able
            if flatten is True or flatten == "True":
                result = result.flatten()
            return result.tolist()
        else:
            return result

    @expose_numpy_array
    def read_binary_datatype_attribute(self,
                                       entity_gid,
                                       dataset_name,
                                       datatype_kwargs='null',
                                       **kwargs):
        return self._read_datatype_attribute(entity_gid, dataset_name,
                                             datatype_kwargs, **kwargs)

    @expose_fragment("flow/genericAdapterFormFields")
    def get_simple_adapter_interface(self,
                                     algorithm_id,
                                     parent_div='',
                                     is_uploader=False):
        """
        AJAX exposed method. Will return only the interface for a adapter, to
        be used when tabs are needed.
        """
        curent_project = common.get_current_project()
        is_uploader = string2bool(is_uploader)
        template_specification = self.get_adapter_template(
            curent_project.id, algorithm_id, is_uploader)
        template_specification[common.KEY_PARENT_DIV] = parent_div
        return self.fill_default_attributes(template_specification)

    @expose_fragment("flow/full_adapter_interface")
    def getadapterinterface(self, project_id, algorithm_id, back_page=None):
        """
        AJAX exposed method. Will return only a piece of a page,
        to be integrated as part in another page.
        """
        template_specification = self.get_adapter_template(
            project_id, algorithm_id, False, back_page)
        template_specification["isCallout"] = True
        return self.fill_default_attributes(template_specification)

    def get_adapter_template(self,
                             project_id,
                             algorithm_id,
                             is_upload=False,
                             back_page=None):
        """
        Get the template for an adapter based on the algo group id.
        """
        if not (project_id and int(project_id) and
                (algorithm_id is not None) and int(algorithm_id)):
            return ""

        algorithm = self.algorithm_service.get_algorithm_by_identifier(
            algorithm_id)
        if is_upload:
            submit_link = "/project/launchloader/" + str(
                project_id) + "/" + str(algorithm_id)
        else:
            submit_link = self.get_url_adapter(algorithm.fk_category,
                                               algorithm.id, back_page)

        current_step = self.context.get_current_substep()
        if current_step is None or str(current_step) != str(algorithm_id):
            self.context.clean_from_session()
        template_specification = self.get_template_for_adapter(
            project_id, algorithm.fk_category, algorithm, submit_link,
            is_upload)
        if template_specification is None:
            return ""
        template_specification[common.KEY_DISPLAY_MENU] = not is_upload
        return template_specification

    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reloadoperation(self, operation_id, **_):
        """Redirect to Operation Input selection page,
        with input data already selected."""
        operation = OperationService.load_operation(operation_id)
        data = parse_json_parameters(operation.parameters)
        self.context.add_adapter_to_session(operation.algorithm, None, data)
        category_id = operation.algorithm.fk_category
        algo_id = operation.fk_from_algo
        raise cherrypy.HTTPRedirect("/flow/" + str(category_id) + "/" +
                                    str(algo_id) + "?not_reset=True")

    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reload_burst_operation(self, operation_id, is_group, **_):
        """
        Find out from which burst was this operation launched. Set that burst as the selected one and
        redirect to the burst page.
        """
        is_group = int(is_group)
        if not is_group:
            operation = OperationService.load_operation(int(operation_id))
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            first_op = ProjectService.get_operations_in_group(op_group)[0]
            operation = OperationService.load_operation(int(first_op.id))
        SimulatorController().copy_simulator_configuration(operation.burst.id)

        raise cherrypy.HTTPRedirect("/burst/")

    @expose_json
    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.
        """
        operation_id = int(operation_id)
        is_group = int(is_group) != 0
        if isinstance(remove_after_stop, str):
            remove_after_stop = bool(remove_after_stop)
        return SimulatorController.cancel_or_remove_operation(
            operation_id, is_group, remove_after_stop)

    def fill_default_attributes(self, template_dictionary, title='-'):
        """
        Overwrite base controller to add required parameters for adapter templates.
        """
        if common.KEY_TITLE not in template_dictionary:
            template_dictionary[common.KEY_TITLE] = title

        if common.KEY_PARENT_DIV not in template_dictionary:
            template_dictionary[common.KEY_PARENT_DIV] = ''
        if common.KEY_PARAMETERS_CONFIG not in template_dictionary:
            template_dictionary[common.KEY_PARAMETERS_CONFIG] = False

        template_dictionary[
            common.KEY_INCLUDE_RESOURCES] = 'flow/included_resources'
        BaseController.fill_default_attributes(self, template_dictionary)
        return template_dictionary

    NEW_SELECTION_NAME = 'New selection'

    def _get_available_selections(self, datatype_gid):
        """
        selection retrieval common to selection component and connectivity selection
        """
        curent_project = common.get_current_project()
        selections = self.algorithm_service.get_selections_for_project(
            curent_project.id, datatype_gid)
        names, sel_values = [], []

        for selection in selections:
            names.append(selection.ui_name)
            sel_values.append(selection.selected_nodes)

        return names, sel_values

    @expose_fragment('visualizers/commons/channel_selector_opts')
    def get_available_selections(self, **data):
        sel_names, sel_values = self._get_available_selections(
            data['datatype_gid'])
        return dict(namedSelections=list(zip(sel_names, sel_values)))

    @expose_json
    def store_measure_points_selection(self, ui_name, **data):
        """
        Save a MeasurePoints selection (new or update existing entity).
        """
        if ui_name and ui_name != self.NEW_SELECTION_NAME:
            sel_project_id = common.get_current_project().id
            # client sends integers as strings:
            selection = json.dumps(
                [int(s) for s in json.loads(data['selection'])])
            datatype_gid = data['datatype_gid']
            self.algorithm_service.save_measure_points_selection(
                ui_name, selection, datatype_gid, sel_project_id)
            return [True, 'Selection saved successfully.']

        else:
            error_msg = self.NEW_SELECTION_NAME + " or empty name are not  valid as selection names."
            return [False, error_msg]

    @expose_fragment(
        "visualizers/pse_discrete/inserting_new_threshold_spec_bar")
    def create_row_of_specs(self, count):
        return dict(id_increment_count=count)

    @expose_json
    def store_pse_filter(self, config_name, **data):
        # this will need to be updated in such a way that the expose_json actually gets used
        ## also this is going to be changed to be storing through the flow service and dao. Stay updated
        try:
            ##this is to check whether there is already an entry in the
            for i, (name, Val) in enumerate(self.PSE_names_list):
                if name == config_name:
                    self.PSE_names_list[i] = (
                        config_name,
                        (data['threshold_value'] + "," +
                         data['threshold_type'] + "," + data['not_presence'])
                    )  # replace the previous occurence of the config name, and carry on.
                    self.get_pse_filters()
                    return [
                        True, 'Selected Text stored, and selection updated'
                    ]
            self.PSE_names_list.append(
                (config_name,
                 (data['threshold_value'] + "," + data['threshold_type'] +
                  "," + data['not_presence'])))
        except AttributeError:
            self.PSE_names_list = [
                (config_name,
                 (data['threshold_value'] + "," + data['threshold_type'] +
                  "," + data['not_presence']))
            ]

        self.get_pse_filters()
        return [True, 'Selected Text stored, and selection updated']

    @expose_fragment("visualizers/commons/channel_selector_opts")
    def get_pse_filters(self):
        try:
            return dict(namedSelections=self.PSE_names_list)
        except AttributeError:
            return dict(
                namedSelections=[]
            )  # this will give us back atleast the New Selection option in the select
        except:
            raise

    @expose_json
    def store_exploration_section(self, val_range, step, dt_group_guid):
        """
        Launching method for further simulations.
        """
        range_list = [float(num) for num in val_range.split(",")]
        step_list = [float(num) for num in step.split(",")]

        datatype_group_ob = ProjectService().get_datatypegroup_by_gid(
            dt_group_guid)
        operation_grp = datatype_group_ob.parent_operation_group
        operation_obj = OperationService.load_operation(
            datatype_group_ob.fk_from_operation)
        parameters = json.loads(operation_obj.parameters)

        range1name, range1_dict = json.loads(operation_grp.range1)
        range2name, range2_dict = json.loads(operation_grp.range2)
        parameters[RANGE_PARAMETER_1] = range1name
        parameters[RANGE_PARAMETER_2] = range2name

        # change the existing simulator parameters to be min max step types
        range1_dict = {
            constants.ATT_MINVALUE: range_list[0],
            constants.ATT_MAXVALUE: range_list[1],
            constants.ATT_STEP: step_list[0]
        }
        range2_dict = {
            constants.ATT_MINVALUE: range_list[2],
            constants.ATT_MAXVALUE: range_list[3],
            constants.ATT_STEP: step_list[1]
        }
        parameters[range1name] = json.dumps(
            range1_dict)  # this is for the x axis parameter
        parameters[range2name] = json.dumps(
            range2_dict)  # this is for the y axis parameter

        OperationService().group_operation_launch(
            common.get_logged_user().id, common.get_current_project(),
            operation_obj.algorithm.id, operation_obj.algorithm.fk_category,
            datatype_group_ob, **parameters)

        return [True, 'Stored the exploration material successfully']
class FlowController(BaseController):
    """
    This class takes care of executing steps in projects.
    """

    def __init__(self):
        BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()


    @expose_page
    @settings
    @context_selected
    def step_analyzers(self):
        """
        Choose exact action/adapter for current step.
        """
        try:
            analyze_category, groups = self.flow_service.get_analyze_groups()
            step_name = analyze_category.displayname.lower()
            template_specification = dict(mainContent="header_menu", section_name=step_name, controlPage=None,
                                          title="Select an analyzer", displayControl=False)
            adapters_list = []
            for adapter_group in groups:

                if len(adapter_group.children) > 1:
                    ids = [str(child.id) for child in adapter_group.children]
                    ids = ','.join(ids)
                    adapter_link = '/flow/show_group_of_algorithms/' + str(analyze_category.id) + "/" + ids
                else:
                    adapter_link = self.get_url_adapter(analyze_category.id, adapter_group.children[0].id)

                adapters_list.append({common.KEY_TITLE: adapter_group.name,
                                      'link': adapter_link,
                                      'description': adapter_group.description,
                                      'subsection': adapter_group.children[0].subsection_name})
            self.analyze_adapters = adapters_list
            template_specification[common.KEY_SUBMENU_LIST] = adapters_list
            return self.fill_default_attributes(template_specification)

        except ValueError:
            message = 'Could not load analyzers!'
            common.set_warning_message(message)
            self.logger.warning(message)
            raise cherrypy.HTTPRedirect('/tvb')


    @expose_page
    @settings
    @context_selected
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu", section_name='connectivity', controlPage=None,
                                      title="Select an algorithm", displayControl=False, subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)


    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            ## This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link


    @expose_page
    @settings
    @context_selected
    def show_group_of_algorithms(self, step_key, algorithm_ids):

        project = common.get_current_project()
        category = self.flow_service.get_category_by_id(step_key)
        algorithms = []
        for i in algorithm_ids.split(','):
            algorithm_id = int(i)
            algorithm = self.flow_service.get_algorithm_by_identifier(algorithm_id)
            algorithm.link = self.get_url_adapter(step_key, algorithm_id)
            algorithm.input_tree = self.flow_service.prepare_adapter(project.id, algorithm)
            algorithms.append(algorithm)

        template_specification = dict(mainContent="flow/algorithms_list", algorithms=algorithms,
                                      title="Select an algorithm", section_name=category.displayname.lower())
        self._populate_section(algorithms[0], template_specification)
        self.fill_default_attributes(template_specification, algorithms[0].group_name)
        return template_specification


    @expose_page
    @settings
    @context_selected
    def prepare_group_launch(self, group_gid, step_key, algorithm_id, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data.pop('range_param_name')
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join(dt.gid for dt in datatypes)
        OperationService().group_operation_launch(common.get_logged_user().id, common.get_current_project().id,
                                                  int(algorithm_id), int(step_key), **data)
        redirect_url = self._compute_back_link('operations', common.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)


    @expose_page
    @settings
    @context_selected
    def default(self, step_key, adapter_key, cancel=False, back_page=None, not_reset=False, **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = common.get_current_project()
        algorithm = self.flow_service.get_algorithm_by_identifier(adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algorithm is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        is_burst = back_page not in ['operations', 'data']
        if cherrypy.request.method == 'POST':
            data[common.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link, step_key, algorithm, **data)
            self._populate_section(algorithm, template_specification, is_burst)
        else:
            if (('Referer' not in cherrypy.request.headers or
                ('Referer' in cherrypy.request.headers and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algorithm.algorithm_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                not_reset = True
            template_specification = self.get_template_for_adapter(project.id, step_key, algorithm,
                                                                   submit_link, not not_reset, is_burst=is_burst)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if common.KEY_SUBMIT_LINK not in template_specification:
            template_specification[common.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[common.KEY_DISPLAY_MENU] = False
        else:
            template_specification[common.KEY_DISPLAY_MENU] = True
            template_specification[common.KEY_BACK_PAGE] = back_page_link

        template_specification[common.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification, algorithm.displayname)
        return template_specification


    @expose_fragment("flow/reduce_dimension_select")
    def gettemplatefordimensionselect(self, entity_gid=None, select_name="", reset_session='False',
                                      parameters_prefix="dimensions", required_dimension=1,
                                      expected_shape="", operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid 
            the GID of the entity for which is displayed the component
        
        select_name
            the name of the parent select. The select in which
            is displayed the entity with the given GID
  
        parameters_prefix 
            a string which will be used for computing the names of the component

        required_dimension
            the expected dimension for the resulted array

        expected_shape and operations
            used for applying conditions on the resulted array
            e.g.: If the resulted array is a 3D array and we want that the length of the second
            dimension to be smaller then 512 then the expected_shape and operations should be:
            ``expected_shape=x,512,x`` and ``operations='x,&lt;,x``
        """
        template_params = {"select_name": "",
                           "data": [],
                           "parameters_prefix": parameters_prefix,
                           "array_shape": "",
                           "required_dimension": required_dimension,
                           "currentDim": "",
                           "required_dim_msg": "",
                           "expected_shape": expected_shape,
                           "operations": operations}

        # if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not string2bool(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items() if k.startswith(starts_with_str))
            dimensions, selected_agg_functions, required_dimension, _ = MappedArray().parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(list(array_shape), dimensions,
                                                                         selected_agg_functions)
                if required_dimension is not None and current_dim != int(required_dimension):
                    template_params["required_dim_msg"] = "Please select a " + str(required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels') and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    # make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions') and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions) == len(array_shape)):
                    #will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = {}
                            for function_key in function:
                                func_dict[function_key] = default_agg_functions[function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in range(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params


    @staticmethod
    def _compute_current_dimension(array_shape, selected_items, selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in range(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim


    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}


    @expose_fragment("flow/type2component/datatype2select_simple")
    def getfiltereddatatypes(self, name, parent_div, tree_session_key, filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            common.set_error_message("Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            # Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][FILTER_TYPE] == 'date':
                try:
                    temp_date = string2date(filters[FILTER_VALUES][i], False)
                    filters[FILTER_VALUES][i] = temp_date
                except ValueError:
                    raise
        # In order for the filter object not to "stack up" on multiple calls to
        # this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        # Get dataTypes that match the filters from DB then populate with values
        values, total_count = InputTreeManager().populate_option_values_for_dtype(
            common.get_current_project().id,
            datatype, new_filter,
            self.context.get_current_step())
        # Create a dictionary that matches what the template expects
        parameters = {ABCAdapter.KEY_NAME: name,
                      ABCAdapter.KEY_FILTERABLE: availablefilter,
                      ABCAdapter.KEY_TYPE: ABCAdapter.TYPE_SELECT,
                      ABCAdapter.KEY_OPTIONS: values,
                      ABCAdapter.KEY_DATATYPE: datatype}

        if total_count > MAXIMUM_DATA_TYPES_DISPLAYED:
            parameters[KEY_WARNING] = WARNING_OVERFLOW

        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and string2bool(str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected in [str(vv['value']) for vv in values]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected

        template_specification = {"inputRow": parameters, "disabled": False,
                                  "parentDivId": parent_div, common.KEY_SESSION_TREE: tree_session_key}
        return self.fill_default_attributes(template_specification)


    def _get_node(self, input_tree, name):
        """
        Given a input tree and a variable name, check to see if any default filters exist.
        """
        for entry in input_tree:
            if (ABCAdapter.KEY_DATATYPE in entry and ABCAdapter.KEY_NAME in entry
                    and str(entry[ABCAdapter.KEY_NAME]) == str(name)):
                return entry
            if entry.get(ABCAdapter.KEY_ATTRIBUTES) is not None:
                in_attr = self._get_node(entry[ABCAdapter.KEY_ATTRIBUTES], name)
                if in_attr is not None:
                    return in_attr
            if entry.get(ABCAdapter.KEY_OPTIONS) is not None:
                in_options = self._get_node(entry[ABCAdapter.KEY_OPTIONS], name)
                if in_options is not None:
                    return in_options
        return None


    def execute_post(self, project_id, submit_url, step_key, algorithm, **data):
        """ Execute HTTP POST on a generic step."""
        errors = None
        adapter_instance = ABCAdapter.build_adapter(algorithm)

        try:
            result = self.flow_service.fire_operation(adapter_instance, common.get_logged_user(), project_id, **data)

            # Store input data in session, for informing user of it.
            step = self.flow_service.get_category_by_id(step_key)
            if not step.rawinput:
                self.context.add_adapter_to_session(None, None, copy.deepcopy(data))

            if isinstance(adapter_instance, ABCDisplayer):
                if isinstance(result, dict):
                    result[common.KEY_OPERATION_ID] = adapter_instance.operation_id
                    return result
                else:
                    common.set_error_message("Invalid result returned from Displayer! Dictionary is expected!")
            else:
                if isinstance(result, list):
                    result = "Launched %s operations." % len(result)
                common.set_important_message(str(result))
        except formencode.Invalid as excep:
            errors = excep.unpack_errors()
        except OperationException as excep1:
            self.logger.exception("Error while executing a Launch procedure:" + excep1.message)
            common.set_error_message(excep1.message)

        previous_step = self.context.get_current_substep()
        should_reset = previous_step is None or data.get(common.KEY_ADAPTER) != previous_step
        template_specification = self.get_template_for_adapter(project_id, step_key, algorithm,
                                                               submit_url, should_reset)
        if (errors is not None) and (template_specification is not None):
            template_specification[common.KEY_ERRORS] = errors
        template_specification[common.KEY_OPERATION_ID] = adapter_instance.operation_id
        return template_specification


    def get_template_for_adapter(self, project_id, step_key, stored_adapter, submit_url, session_reset=True,
                                 is_burst=True):
        """ Get Input HTML Interface template or a given adapter """
        try:
            if session_reset:
                self.context.clean_from_session()

            group = None
            # Cache some values in session, for performance
            previous_tree = self.context.get_current_input_tree()
            previous_sub_step = self.context.get_current_substep()
            if not session_reset and previous_tree is not None and previous_sub_step == stored_adapter.id:
                adapter_interface = previous_tree
            else:
                adapter_interface = self.flow_service.prepare_adapter(project_id, stored_adapter)
                self.context.add_adapter_to_session(stored_adapter, adapter_interface)

            category = self.flow_service.get_category_by_id(step_key)
            title = "Fill parameters for step " + category.displayname.lower()
            if group:
                title = title + " - " + group.displayname

            current_defaults = self.context.get_current_default()
            if current_defaults is not None:
                # Change default values in tree, according to selected input
                adapter_interface = InputTreeManager.fill_defaults(adapter_interface, current_defaults)

            template_specification = dict(submitLink=submit_url, inputList=adapter_interface, title=title)
            self._populate_section(stored_adapter, template_specification, is_burst)
            return template_specification
        except OperationException as oexc:
            self.logger.error("Inconsistent Adapter")
            self.logger.exception(oexc)
            common.set_warning_message('Inconsistent Adapter!  Please review the link (development problem)!')
        return None


    @cherrypy.expose
    @handle_error(redirect=False)
    @check_user
    def readserverstaticfile(self, coded_path):
        """
        Retrieve file from Local storage, having a File System Path.
        """
        try:
            with open(url2path(coded_path), "rb") as f:
                return f.read()
        except Exception as excep:
            self.logger.error("Could not retrieve file from path:" + str(coded_path))
            self.logger.exception(excep)


    def _read_datatype_attribute(self, entity_gid, dataset_name, datatype_kwargs='null', **kwargs):
        self.logger.debug("Starting to read HDF5: " + entity_gid + "/" + dataset_name + "/" + str(kwargs))
        entity = ABCAdapter.load_entity_by_gid(entity_gid)

        datatype_kwargs = json.loads(datatype_kwargs)
        if datatype_kwargs:
            for key, value in six.iteritems(datatype_kwargs):
                kwargs[key] = ABCAdapter.load_entity_by_gid(value)

        result = getattr(entity, dataset_name)
        if callable(result):
            if kwargs:
                result = result(**kwargs)
            else:
                result = result()
        return result


    @expose_json
    def read_datatype_attribute(self, entity_gid, dataset_name, flatten=False, datatype_kwargs='null', **kwargs):
        """
        Retrieve from a given DataType a property or a method result.

        :returns: JSON representation of the attribute.
        :param entity_gid: GID for DataType entity
        :param dataset_name: name of the dataType property /method 
        :param flatten: result should be flatten before return (use with WebGL data mainly e.g vertices/triangles)
            Ignored if the attribute is not an ndarray
        :param datatype_kwargs: if passed, will contain a dictionary of type {'name' : 'gid'}, and for each such
            pair, a load_entity will be performed and kwargs will be updated to contain the result
        :param kwargs: extra parameters to be passed when dataset_name is method.

        """
        result = self._read_datatype_attribute(entity_gid, dataset_name, datatype_kwargs, **kwargs)

        if isinstance(result, numpy.ndarray):
            # for ndarrays honor the flatten kwarg and convert to lists as ndarrs are not json-able
            if flatten is True or flatten == "True":
                result = result.flatten()
            return result.tolist()
        else:
            return result


    @expose_numpy_array
    def read_binary_datatype_attribute(self, entity_gid, dataset_name, datatype_kwargs='null', **kwargs):
        return self._read_datatype_attribute(entity_gid, dataset_name, datatype_kwargs, **kwargs)


    @expose_fragment("flow/genericAdapterFormFields")
    def get_simple_adapter_interface(self, algorithm_id, parent_div='', is_uploader=False):
        """
        AJAX exposed method. Will return only the interface for a adapter, to
        be used when tabs are needed.
        """
        curent_project = common.get_current_project()
        is_uploader = string2bool(is_uploader)
        template_specification = self.get_adapter_template(curent_project.id, algorithm_id, is_uploader)
        template_specification[common.KEY_PARENT_DIV] = parent_div
        return self.fill_default_attributes(template_specification)


    @expose_fragment("flow/full_adapter_interface")
    def getadapterinterface(self, project_id, algorithm_id, back_page=None):
        """
        AJAX exposed method. Will return only a piece of a page, 
        to be integrated as part in another page.
        """
        template_specification = self.get_adapter_template(project_id, algorithm_id, False, back_page)
        template_specification["isCallout"] = True
        return self.fill_default_attributes(template_specification)


    def get_adapter_template(self, project_id, algorithm_id, is_upload=False, back_page=None):
        """
        Get the template for an adapter based on the algo group id.
        """
        if not (project_id and int(project_id) and (algorithm_id is not None) and int(algorithm_id)):
            return ""

        algorithm = self.flow_service.get_algorithm_by_identifier(algorithm_id)
        if is_upload:
            submit_link = "/project/launchloader/" + str(project_id) + "/" + str(algorithm_id)
        else:
            submit_link = self.get_url_adapter(algorithm.fk_category, algorithm.id, back_page)

        current_step = self.context.get_current_substep()
        if current_step is None or str(current_step) != str(algorithm_id):
            self.context.clean_from_session()
        template_specification = self.get_template_for_adapter(project_id, algorithm.fk_category, algorithm,
                                                               submit_link, is_upload)
        if template_specification is None:
            return ""
        template_specification[common.KEY_DISPLAY_MENU] = not is_upload
        return template_specification


    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reloadoperation(self, operation_id, **_):
        """Redirect to Operation Input selection page, 
        with input data already selected."""
        operation = self.flow_service.load_operation(operation_id)
        data = parse_json_parameters(operation.parameters)
        self.context.add_adapter_to_session(operation.algorithm, None, data)
        category_id = operation.algorithm.fk_category
        algo_id = operation.fk_from_algo
        raise cherrypy.HTTPRedirect("/flow/" + str(category_id) + "/" + str(algo_id) + "?not_reset=True")


    @cherrypy.expose
    @handle_error(redirect=True)
    @context_selected
    def reload_burst_operation(self, operation_id, is_group, **_):
        """
        Find out from which burst was this operation launched. Set that burst as the selected one and 
        redirect to the burst page.
        """
        is_group = int(is_group)
        if not is_group:
            operation = self.flow_service.load_operation(int(operation_id))
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            first_op = ProjectService.get_operations_in_group(op_group)[0]
            operation = self.flow_service.load_operation(int(first_op.id))
        operation.burst.prepare_after_load()
        common.add2session(common.KEY_BURST_CONFIG, operation.burst)
        raise cherrypy.HTTPRedirect("/burst/")


    @expose_json
    def stop_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.
        """
        operation_service = OperationService()
        result = False
        if int(is_group) == 0:
            result = operation_service.stop_operation(operation_id)
            if remove_after_stop:
                ProjectService().remove_operation(operation_id)
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            operations_in_group = ProjectService.get_operations_in_group(op_group)
            for operation in operations_in_group:
                tmp_res = operation_service.stop_operation(operation.id)
                if remove_after_stop:
                    ProjectService().remove_operation(operation.id)
                result = result or tmp_res
        return result


    @expose_json
    def stop_burst_operation(self, operation_id, is_group, remove_after_stop=False):
        """
        For a given operation id that is part of a burst just stop the given burst.
        :returns True when stopped operation was successfully.
        """
        operation_id = int(operation_id)
        if int(is_group) == 0:
            operation = self.flow_service.load_operation(operation_id)
        else:
            op_group = ProjectService.get_operation_group_by_id(operation_id)
            first_op = ProjectService.get_operations_in_group(op_group)[0]
            operation = self.flow_service.load_operation(int(first_op.id))

        try:
            burst_service = BurstService()
            result = burst_service.stop_burst(operation.burst)
            if remove_after_stop:
                current_burst = common.get_from_session(common.KEY_BURST_CONFIG)
                if current_burst and current_burst.id == operation.burst.id:
                    common.remove_from_session(common.KEY_BURST_CONFIG)
                result = burst_service.cancel_or_remove_burst(operation.burst.id) or result

            return result
        except Exception as ex:
            self.logger.exception(ex)
            return False


    def fill_default_attributes(self, template_dictionary, title='-'):
        """
        Overwrite base controller to add required parameters for adapter templates.
        """
        if common.KEY_TITLE not in template_dictionary:
            template_dictionary[common.KEY_TITLE] = title

        if common.KEY_PARENT_DIV not in template_dictionary:
            template_dictionary[common.KEY_PARENT_DIV] = ''
        if common.KEY_PARAMETERS_CONFIG not in template_dictionary:
            template_dictionary[common.KEY_PARAMETERS_CONFIG] = False

        template_dictionary[common.KEY_INCLUDE_RESOURCES] = 'flow/included_resources'
        BaseController.fill_default_attributes(self, template_dictionary)
        return template_dictionary


    ##### Below this point are operations that might be moved to different #####
    ##### controller                                                       #####

    NEW_SELECTION_NAME = 'New selection'

    def _get_available_selections(self, datatype_gid):
        """
        selection retrieval common to selection component and connectivity selection
        """
        curent_project = common.get_current_project()
        selections = self.flow_service.get_selections_for_project(curent_project.id, datatype_gid)
        names, sel_values = [], []

        for selection in selections:
            names.append(selection.ui_name)
            sel_values.append(selection.selected_nodes)

        return names, sel_values


    @expose_fragment('visualizers/commons/channel_selector_opts')
    def get_available_selections(self, **data):
        sel_names, sel_values = self._get_available_selections(data['datatype_gid'])
        return dict(namedSelections=zip(sel_names, sel_values))


    @expose_json
    def store_measure_points_selection(self, ui_name, **data):
        """
        Save a MeasurePoints selection (new or update existing entity).
        """
        if ui_name and ui_name != self.NEW_SELECTION_NAME:
            sel_project_id = common.get_current_project().id
            # client sends integers as strings:
            selection = json.dumps([int(s) for s in json.loads(data['selection'])])
            datatype_gid = data['datatype_gid']
            self.flow_service.save_measure_points_selection(ui_name, selection, datatype_gid, sel_project_id)
            return [True, 'Selection saved successfully.']

        else:
            error_msg = self.NEW_SELECTION_NAME + " or empty name are not  valid as selection names."
            return [False, error_msg]


    @expose_fragment("visualizers/pse_discrete/inserting_new_threshold_spec_bar")
    def create_row_of_specs(self, count):
        return dict(id_increment_count=count)


    @expose_json
    def store_pse_filter(self, config_name, **data):
        # this will need to be updated in such a way that the expose_json actually gets used
        ## also this is going to be changed to be storing through the flow service and dao. Stay updated
        try:
            ##this is to check whether there is already an entry in the
            for i, (name, Val) in enumerate(self.PSE_names_list):
                if name == config_name:
                    self.PSE_names_list[i] = (config_name, (
                        data['threshold_value'] + "," + data['threshold_type'] + "," + data[
                            'not_presence']))  # replace the previous occurence of the config name, and carry on.
                    self.get_pse_filters()
                    return [True, 'Selected Text stored, and selection updated']
            self.PSE_names_list.append(
                (config_name, (data['threshold_value'] + "," + data['threshold_type'] + "," + data['not_presence'])))
        except AttributeError:
            self.PSE_names_list = [
                (config_name, (data['threshold_value'] + "," + data['threshold_type'] + "," + data['not_presence']))]

        self.get_pse_filters()
        return [True, 'Selected Text stored, and selection updated']


    @expose_fragment("visualizers/commons/channel_selector_opts")
    def get_pse_filters(self):
        try:
            return dict(namedSelections=self.PSE_names_list)
        except AttributeError:
            return dict(namedSelections=[])  # this will give us back atleast the New Selection option in the select
        except:
            raise

    @expose_json
    def store_exploration_section(self, val_range, step, dt_group_guid):
        """
        Launching method for further simulations.
        """
        range_list = [float(num) for num in val_range.split(",")]
        step_list = [float(num) for num in step.split(",")]

        datatype_group_ob = ProjectService().get_datatypegroup_by_gid(dt_group_guid)
        operation_grp = datatype_group_ob.parent_operation_group
        operation_obj = self.flow_service.load_operation(datatype_group_ob.fk_from_operation)
        parameters = json.loads(operation_obj.parameters)

        range1name, range1_dict = json.loads(operation_grp.range1)
        range2name, range2_dict = json.loads(operation_grp.range2)
        parameters[RANGE_PARAMETER_1] = range1name
        parameters[RANGE_PARAMETER_2] = range2name

        ##change the existing simulator parameters to be min max step types
        range1_dict = {constants.ATT_MINVALUE: range_list[0],
                       constants.ATT_MAXVALUE: range_list[1],
                       constants.ATT_STEP: step_list[0]}
        range2_dict = {constants.ATT_MINVALUE: range_list[2],
                       constants.ATT_MAXVALUE: range_list[3],
                       constants.ATT_STEP: step_list[1]}
        parameters[range1name] = json.dumps(range1_dict)  # this is for the x axis parameter
        parameters[range2name] = json.dumps(range2_dict)  # this is for the y axis parameter

        OperationService().group_operation_launch(common.get_logged_user().id, common.get_current_project().id,
                                                  operation_obj.algorithm.id, operation_obj.algorithm.fk_category,
                                                  datatype_group_ob, **parameters)

        return [True, 'Stored the exploration material successfully']
class FlowController(base.BaseController):
    """
    This class takes care of executing steps in projects.
    """

    def __init__(self):
        base.BaseController.__init__(self)
        self.context = SelectedAdapterContext()
        self.files_helper = FilesHelper()


    @cherrypy.expose
    @using_template('base_template')
    @logged()
    @base.settings()
    @context_selected()
    def step(self, step_key=None):
        """
        Choose exact action/adapter for current step.
        """
        category = self.flow_service.get_category_by_id(step_key)
        if category is None:
            message = 'Inconsistent Step Name! Please excuse the wrong link!'
            base.set_warning_message(message)
            self.logger.warning(message + '- Wrong step:' + str(step_key))
            raise cherrypy.HTTPRedirect('/tvb')

        step_name = category.displayname.lower()
        template_specification = dict(mainContent="header_menu", section_name=step_name, controlPage=None,
                                      title="Select an algorithm", displayControl=False)
        adapters_list = []
        for algo_group in self.flow_service.get_groups_for_categories([category]):
            if algo_group.ui_display < 0:
                continue
            adapter_link = self.get_url_adapter(step_key, algo_group.id)
            adapters_list.append({base.KEY_TITLE: algo_group.displayname,
                                  'link': adapter_link,
                                  'description': algo_group.description,
                                  'subsection': algo_group.subsection_name})
        self.analyze_adapters = adapters_list
        template_specification[base.KEY_SUBMENU_LIST] = adapters_list
        return self.fill_default_attributes(template_specification)


    @cherrypy.expose
    @using_template('base_template')
    @base.settings()
    @logged()
    @context_selected()
    def step_connectivity(self):
        """
        Display menu for Connectivity Footer tab.
        """
        template_specification = dict(mainContent="header_menu", section_name='connectivity', controlPage=None,
                                      title="Select an algorithm", displayControl=False, subsection_name='step',
                                      submenu_list=self.connectivity_submenu)
        return self.fill_default_attributes(template_specification)


    @staticmethod
    def _compute_back_link(back_indicator, project):
        """
        Based on a simple indicator, compute URL for anchor BACK.
        """
        if back_indicator is None:
            ## This applies to Connectivity and other visualizers when RELAUNCH button is used from Operation page.
            back_page_link = None
        elif back_indicator == 'burst':
            back_page_link = "/burst"
        elif back_indicator == 'operations':
            back_page_link = '/project/viewoperations/' + str(project.id)
        else:
            back_page_link = '/project/editstructure/' + str(project.id)
        return back_page_link


    @cherrypy.expose
    @base.settings()
    @logged()
    @context_selected()
    @using_template('base_template')
    def prepare_group_launch(self, group_gid, step_key, adapter_key, **data):
        """
        Receives as input a group gid and an algorithm given by category and id, along
        with data that gives the name of the required input parameter for the algorithm.
        Having these generate a range of GID's for all the DataTypes in the group and
        launch a new operation group.
        """
        prj_service = ProjectService()
        dt_group = prj_service.get_datatypegroup_by_gid(group_gid)
        datatypes = prj_service.get_datatypes_from_datatype_group(dt_group.id)
        range_param_name = data['range_param_name']
        del data['range_param_name']
        data[RANGE_PARAMETER_1] = range_param_name
        data[range_param_name] = ','.join([dt.gid for dt in datatypes])
        OperationService().group_operation_launch(base.get_logged_user().id, base.get_current_project().id, 
                                                  int(adapter_key), int(step_key), **data)
        redirect_url = self._compute_back_link('operations', base.get_current_project())
        raise cherrypy.HTTPRedirect(redirect_url)
    

    @cherrypy.expose
    @using_template('base_template')
    @base.settings()
    @logged()
    @context_selected()
    def default(self, step_key, adapter_key, cancel=False, back_page=None, not_reset=False, **data):
        """
        Render a specific adapter.
        'data' are arguments for POST
        """
        project = base.get_current_project()
        algo_group = self.flow_service.get_algo_group_by_identifier(adapter_key)
        back_page_link = self._compute_back_link(back_page, project)

        if algo_group is None:
            raise cherrypy.HTTPRedirect("/tvb?error=True")

        if cherrypy.request.method == 'POST' and cancel:
            raise cherrypy.HTTPRedirect(back_page_link)

        submit_link = self.get_url_adapter(step_key, adapter_key, back_page)
        if cherrypy.request.method == 'POST':
            back_indicator = back_page if back_page == 'burst' else 'operations'
            success_url = self._compute_back_link(back_indicator, project)
            data[base.KEY_ADAPTER] = adapter_key
            template_specification = self.execute_post(project.id, submit_link, success_url,
                                                       step_key, algo_group, **data)
        else:
            if (('Referer' not in cherrypy.request.headers or
                ('Referer' in cherrypy.request.headers and 'step' not in cherrypy.request.headers['Referer']))
                    and 'View' in algo_group.group_category.displayname):
                # Avoid reset in case of Visualizers, as a supplementary GET
                # might be enforced by MPLH5 on FF.
                not_reset = True
            template_specification = self.get_template_for_adapter(project.id, step_key, algo_group,
                                                                   submit_link, not not_reset)
        if template_specification is None:
            raise cherrypy.HTTPRedirect('/tvb')

        if KEY_CONTROLLS not in template_specification:
            template_specification[KEY_CONTROLLS] = None
        if base.KEY_SUBMIT_LINK not in template_specification:
            template_specification[base.KEY_SUBMIT_LINK] = submit_link
        if KEY_CONTENT not in template_specification:
            template_specification[KEY_CONTENT] = "flow/full_adapter_interface"
            template_specification[base.KEY_DISPLAY_MENU] = False
        else:
            template_specification[base.KEY_DISPLAY_MENU] = True
            template_specification[base.KEY_BACK_PAGE] = back_page_link

        template_specification[base.KEY_ADAPTER] = adapter_key
        template_specification[ABCDisplayer.KEY_IS_ADAPTER] = True
        self.fill_default_attributes(template_specification, algo_group)
        if (back_page is not None and back_page in ['operations', 'data'] and
            not (base.KEY_SECTION in template_specification
                 and template_specification[base.KEY_SECTION] == 'connectivity')):
            template_specification[base.KEY_SECTION] = 'project'
        return template_specification


    @cherrypy.expose
    @using_template("flow/reduce_dimension_select")
    @logged()
    def gettemplatefordimensionselect(self, entity_gid=None, select_name="", reset_session='False',
                                      parameters_prefix="dimensions", required_dimension=1,
                                      expected_shape="", operations=""):
        """
        Returns the HTML which contains the selects components which allows the user
        to reduce the dimension of a multi-dimensional array.

        We try to obtain the aggregation_functions from the entity, which is a list of lists.
        For each dimension should be a list with the supported aggregation functions. We
        create a DICT for each of those lists. The key will be the name of the function and
        the value will be its label.

        entity_gid 
            the GID of the entity for which is displayed the component
        
        select_name
            the name of the parent select. The select in which
            is displayed the entity with the given GID
  
        parameters_prefix 
            a string which will be used for computing the names of the component

        required_dimension
            the expected dimension for the resulted array

        expected_shape and operations
            used for applying conditions on the resulted array
            e.g.: If the resulted array is a 3D array and we want that the length of the second
            dimension to be smaller then 512 then the expected_shape and operations should be:
            ``expected_shape=x,512,x`` and ``operations='x,&lt;,x``
        """
        template_params = dict()
        template_params["select_name"] = ""
        template_params["data"] = []
        template_params["parameters_prefix"] = parameters_prefix
        template_params["array_shape"] = ""
        template_params["required_dimension"] = required_dimension
        template_params["currentDim"] = ""
        template_params["required_dim_msg"] = ""
        template_params["expected_shape"] = expected_shape
        template_params["operations"] = operations

        #if reload => populate the selected values
        session_dict = self.context.get_current_default()
        dimensions = {1: [0], 3: [0]}
        selected_agg_functions = {}
        if not eval(str(reset_session)) and session_dict is not None:
            starts_with_str = select_name + "_" + parameters_prefix + "_"
            ui_sel_items = dict((k, v) for k, v in session_dict.items() if k.startswith(starts_with_str))
            dimensions, selected_agg_functions, required_dimension, _ = MappedArray().parse_selected_items(ui_sel_items)
        template_params["selected_items"] = dimensions
        template_params["selected_functions"] = selected_agg_functions

        aggregation_functions = []
        default_agg_functions = self.accepted__aggregation_functions()
        labels_set = ["Time", "Channel", "Line"]
        if entity_gid is not None:
            actual_entity = ABCAdapter.load_entity_by_gid(entity_gid)
            if hasattr(actual_entity, 'shape'):
                array_shape = actual_entity.shape
                new_shape, current_dim = self._compute_current_dimension(list(array_shape), dimensions,
                                                                         selected_agg_functions)
                if required_dimension is not None and current_dim != int(required_dimension):
                    template_params["required_dim_msg"] = "Please select a " + str(required_dimension) + "D array"
                if not current_dim:
                    template_params["currentDim"] = "1 element"
                else:
                    template_params["currentDim"] = str(current_dim) + "D array"
                template_params["array_shape"] = json.dumps(new_shape)
                if hasattr(actual_entity, 'dimensions_labels') and actual_entity.dimensions_labels is not None:
                    labels_set = actual_entity.dimensions_labels
                    #make sure there exists labels for each dimension
                    while len(labels_set) < len(array_shape):
                        labels_set.append("Undefined")
                if (hasattr(actual_entity, 'aggregation_functions') and actual_entity.aggregation_functions is not None
                        and len(actual_entity.aggregation_functions) == len(array_shape)):
                    #will be a list of lists of aggregation functions
                    defined_functions = actual_entity.aggregation_functions
                    for function in defined_functions:
                        if not len(function):
                            aggregation_functions.append({})
                        else:
                            func_dict = dict()
                            for function_key in function:
                                func_dict[function_key] = default_agg_functions[function_key]
                            aggregation_functions.append(func_dict)
                else:
                    for _ in array_shape:
                        aggregation_functions.append(default_agg_functions)
                result = []
                for i, shape in enumerate(array_shape):
                    labels = []
                    values = []
                    for j in xrange(shape):
                        labels.append(labels_set[i] + " " + str(j))
                        values.append(entity_gid + "_" + str(i) + "_" + str(j))
                    result.append([labels, values, aggregation_functions[i]])
                template_params["select_name"] = select_name
                template_params["data"] = result
                return template_params

        return template_params


    @staticmethod
    def _compute_current_dimension(array_shape, selected_items, selected_functions):
        """
        If the user reloads an operation we have to compute the current dimension of the array
        and also the shape of the array based on his selections
        """
        current_dim = len(array_shape)
        for i in xrange(len(array_shape)):
            if i in selected_items and len(selected_items[i]) > 0:
                array_shape[i] = len(selected_items[i])
                if len(selected_items[i]) == 1:
                    current_dim -= 1
            if i in selected_functions and selected_functions[i] != 'none':
                array_shape[i] = 1
                if i not in selected_items or len(selected_items[i]) > 1:
                    current_dim -= 1
        return array_shape, current_dim


    @staticmethod
    def accepted__aggregation_functions():
        """
        Returns the list of aggregation functions that may be
        applied on arrays.
        """
        return {"sum": "Sum", "average": "Average"}


    @cherrypy.expose
    @using_template("flow/type2component/datatype2select_simple")
    @logged()
    def getfiltereddatatypes(self, name, parent_div, tree_session_key, filters):
        """
        Given the name from the input tree, the dataType required and a number of
        filters, return the available dataType that satisfy the conditions imposed.
        """
        previous_tree = self.context.get_session_tree_for_key(tree_session_key)
        if previous_tree is None:
            base.set_error_message("Adapter Interface not in session for filtering!")
            raise cherrypy.HTTPRedirect("/tvb?error=True")
        current_node = self._get_node(previous_tree, name)
        if current_node is None:
            raise Exception("Could not find node :" + name)
        datatype = current_node[ABCAdapter.KEY_DATATYPE]

        filters = json.loads(filters)
        availablefilter = json.loads(FilterChain.get_filters_for_type(datatype))
        for i, filter_ in enumerate(filters[FILTER_FIELDS]):
            #Check for filter input of type 'date' as these need to be converted
            if filter_ in availablefilter and availablefilter[filter_][FILTER_TYPE] == 'date':
                try:
                    filter_ = string2date(filter_, False)
                    filters[FILTER_VALUES][i] = filter_
                except ValueError, excep:
                    raise excep
        #In order for the filter object not to "stack up" on multiple calls to
        #this method, create a deepCopy to work with
        if ABCAdapter.KEY_CONDITION in current_node:
            new_filter = copy.deepcopy(current_node[ABCAdapter.KEY_CONDITION])
        else:
            new_filter = FilterChain()
        new_filter.fields.extend(filters[FILTER_FIELDS])
        new_filter.operations.extend(filters[FILTER_OPERATIONS])
        new_filter.values.extend(filters[FILTER_VALUES])
        #Get dataTypes that match the filters from DB then populate with values
        datatypes = self.flow_service.get_available_datatypes(base.get_current_project().id, datatype, new_filter)
        values = self.flow_service.populate_values(datatypes, datatype, self.context.get_current_step())
        #Create a dictionary that matches what the template expects
        parameters = dict()
        parameters[ABCAdapter.KEY_NAME] = name
        if ABCAdapter.KEY_REQUIRED in current_node:
            parameters[ABCAdapter.KEY_REQUIRED] = current_node[ABCAdapter.KEY_REQUIRED]
            if len(values) > 0 and eval(str(parameters[ABCAdapter.KEY_REQUIRED])):
                parameters[ABCAdapter.KEY_DEFAULT] = str(values[-1][ABCAdapter.KEY_VALUE])
        previous_selected = self.context.get_current_default(name)
        if previous_selected is not None and previous_selected in [str(vv['value']) for vv in values]:
            parameters[ABCAdapter.KEY_DEFAULT] = previous_selected
        parameters[ABCAdapter.KEY_FILTERABLE] = availablefilter
        parameters[ABCAdapter.KEY_TYPE] = ABCAdapter.TYPE_SELECT
        parameters[ABCAdapter.KEY_OPTIONS] = values
        parameters[ABCAdapter.KEY_DATATYPE] = datatype
        template_specification = {"inputRow": parameters, "disabled": False,
                                  "parentDivId": parent_div, base.KEY_SESSION_TREE: tree_session_key}
        return self.fill_default_attributes(template_specification)