From a9a76f6078f7317699ef857d65f95b30fae316bd Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 13 Jan 2021 16:34:31 +0200 Subject: [PATCH 1/9] TVB-2757: Partial solution for solving application of runtime filters by replacing the whole form. --- .../tvb/adapters/visualizers/connectivity.py | 18 +++++-- framework_tvb/tvb/core/neotraits/forms.py | 8 ++- .../web/controllers/flow_controller.py | 42 +++++++++++++++ .../tvb/interfaces/web/static/js/filters.js | 53 +++++++++++++++++-- .../form_fields/datatype_select_field.html | 19 ++++++- 5 files changed, 129 insertions(+), 11 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/connectivity.py b/framework_tvb/tvb/adapters/visualizers/connectivity.py index efbb0b6896..3c83da6852 100644 --- a/framework_tvb/tvb/adapters/visualizers/connectivity.py +++ b/framework_tvb/tvb/adapters/visualizers/connectivity.py @@ -114,8 +114,8 @@ class ConnectivityViewerForm(ABCAdapterForm): def __init__(self): super(ConnectivityViewerForm, self).__init__() - self.connectivity = TraitDataTypeSelectField(ConnectivityViewerModel.connectivity, name='input_data', - conditions=self.get_filters()) + self.connectivity_data = TraitDataTypeSelectField(ConnectivityViewerModel.connectivity, + name='connectivity_data', conditions=self.get_filters()) surface_conditions = FilterChain(fields=[FilterChain.datatype + '.surface_type'], operations=["=="], values=['Cortical Surface']) self.surface_data = TraitDataTypeSelectField(ConnectivityViewerModel.surface_data, name='surface_data', @@ -125,10 +125,12 @@ def __init__(self): colors_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.colors = TraitDataTypeSelectField(ConnectivityViewerModel.colors, name='colors', - conditions=colors_conditions) + conditions=colors_conditions, + runtime_conditions=self.get_runtime_filters()) rays_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) - self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=rays_conditions) + self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=rays_conditions, + runtime_conditions=self.get_runtime_filters()) @staticmethod def get_view_model(): @@ -142,6 +144,11 @@ def get_required_datatype(): def get_filters(): return None + @staticmethod + def get_runtime_filters(): + return {'input_data': FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], + values=[ConnectivityIndex])} + @staticmethod def get_input_name(): return "input_data" @@ -258,7 +265,8 @@ def _compute_connectivity_global_params(self, connectivity): path_labels = SurfaceURLGenerator.paths2url(conn_gid, 'ordered_labels') path_hemisphere_order_indices = SurfaceURLGenerator.paths2url(conn_gid, 'hemisphere_order_indices') - algo = AlgorithmService().get_algorithm_by_module_and_class(CONNECTIVITY_CREATOR_MODULE, CONNECTIVITY_CREATOR_CLASS) + algo = AlgorithmService().get_algorithm_by_module_and_class(CONNECTIVITY_CREATOR_MODULE, + CONNECTIVITY_CREATOR_CLASS) submit_url = '/{}/{}/{}'.format(SurfaceURLGenerator.FLOW, algo.fk_category, algo.id) global_pages = dict(controlPage="connectivity/top_right_controls") diff --git a/framework_tvb/tvb/core/neotraits/forms.py b/framework_tvb/tvb/core/neotraits/forms.py index 6f9d950101..a7531ef3dd 100644 --- a/framework_tvb/tvb/core/neotraits/forms.py +++ b/framework_tvb/tvb/core/neotraits/forms.py @@ -134,7 +134,7 @@ class TraitDataTypeSelectField(TraitField): def __init__(self, trait_attribute, name=None, conditions=None, draw_dynamic_conditions_buttons=True, has_all_option=False, - show_only_all_option=False): + show_only_all_option=False, runtime_conditions=None): super(TraitDataTypeSelectField, self).__init__(trait_attribute, name) if issubclass(type(trait_attribute), DataTypeGidAttr): @@ -151,6 +151,7 @@ def __init__(self, trait_attribute, name=None, conditions=None, self.has_all_option = has_all_option self.show_only_all_option = show_only_all_option self.datatype_options = [] + self.runtime_conditions = runtime_conditions def from_trait(self, trait, f_name): if hasattr(trait, f_name): @@ -166,6 +167,10 @@ def get_dynamic_filters(self): def get_form_filters(self): return self.conditions + @property + def get_runtime_filters(self): + return self.runtime_conditions + def options(self): if not self.required: choice = None @@ -414,6 +419,7 @@ def __str__(self): class Form(object): + template = 'form_fields/form.html' def __init__(self): self.errors = [] diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index 42d1b40a84..218b6d3f9d 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -50,6 +50,7 @@ from tvb.core.entities.file.files_helper import FilesHelper from tvb.core.entities.filters.chain import FilterChain from tvb.core.entities.load import load_entity_by_gid +from tvb.core.entities.storage import dao from tvb.core.neocom import h5 from tvb.core.neocom.h5 import REGISTRY from tvb.core.neotraits.forms import TraitDataTypeSelectField @@ -282,6 +283,47 @@ def get_filtered_datatypes(self, dt_module, dt_class, filters, has_all_option, h return {'options': select_field.options()} + @expose_fragment('form_fields/form') + @settings + @context_selected + def get_runtime_filtered_form(self, algorithm_id, filters): + algorithm = dao.get_algorithm_by_id(algorithm_id) + adapter = getattr(sys.modules[algorithm.module], algorithm.classname)() + form = adapter.get_form_class()() + + filter_dict = json.loads(filters) + project_id = common.get_current_project().id + + fields = [] + operations = [] + values = [] + + runtime_fields = [] + runtime_operations = [] + runtime_values = [] + + for key, value in filter_dict.items(): + select_field_attr = getattr(form, key) + for i in range(len(value['fields'])): + if value['isRuntimeFilter'][i]: + runtime_fields.append(value['fields'][i]) + runtime_operations.append(value['operations'][i]) + runtime_values.append(value['values'][i]) + else: + fields.append(value['fields'][i]) + operations.append(value['operations'][i]) + values.append(value['values'][i]) + + conditions = FilterChain(fields=fields, operations=operations, values=values) + select_field_attr.conditions = conditions + + runtime_conditions = FilterChain(fields=runtime_fields, operations=runtime_operations, + values=runtime_values) + select_field_attr.runtime_conditions = runtime_conditions + self.algorithm_service.fill_selectfield_with_datatypes(select_field_attr, project_id) + + return {'adapter_form': form} + def execute_post(self, project_id, submit_url, step_key, algorithm, **data): """ Execute HTTP POST on a generic step.""" errors = None diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index 4b00837976..8556a498dc 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -81,30 +81,43 @@ function addFilter(div_id, filters) { } /** gather all the data from the filters */ -function _FIL_gatherData(divId){ +function _FIL_gatherData(divId, uiValue){ var children = $('#'+divId).children('div'); var fields = []; var operations = []; var values = []; + var isRuntimeFilter = []; + var triggersFiltering = false; for (var i = 0; i < children.length; i++) { var elem = children[i].children; //Get info about the filters. if (elem[3].value.trim().length > 0) { + var value = elem[3].value.trim(); + + if(children[i].className.endsWith('runtime_trigger')){ + value = uiValue; + triggersFiltering = true; + isRuntimeFilter.push(true); + }else{ + isRuntimeFilter.push(false); + } + fields.push(elem[1].value); operations.push(elem[2].value); - values.push(elem[3].value.trim()); + values.push(value); displayMessage("Filters processed"); } else { displayMessage("Please set a value for all the filters.", "errorMessage"); return; } } - if (fields.length === 0 && operations.length === 0 && values.length === 0) { + if (fields.length === 0 && operations.length === 0 && values.length === 0 && triggersFiltering === false) { displayMessage("Cleared filters"); } - return { fields: fields, operations: operations, values: values}; + return { filters: {fields: fields, operations: operations, values: values, isRuntimeFilter: isRuntimeFilter}, + triggersFiltering: triggersFiltering}; } function applyFilters(datatypeIndex, divId, name, gatheredData) { @@ -162,6 +175,38 @@ function applyFilters(datatypeIndex, divId, name, gatheredData) { }); } +function applyRuntimeFilters(name, selected_value){ + var form = $('#' + name).closest('form'); + let form_action = form[0].action; + let algorithm_id_start = form_action.lastIndexOf('/'); + let algorthimm_id = form_action.substring(algorithm_id_start + 1, form_action.length); + + let select_fields = form.find('select.dataset-selector'); + var fields_and_filters = {} + + let filters; + for (var i = 0; i < select_fields.length; i++) { + filter_values = _FIL_gatherData(select_fields[i].id + 'data_select', selected_value); + fields_and_filters[select_fields[i].id] = filter_values.filters; + } + + if(filter_values.triggersFiltering) { + doAjaxCall({ + type: 'POST', + url: "/flow/get_runtime_filtered_form/" + algorthimm_id + '/' + $.toJSON(fields_and_filters), + success: function (response) { + const t = document.createRange().createContextualFragment(response); + + let adapters_div = $('.adaptersDiv'); + adapters_div.children('fieldset').replaceWith(t); + }, + error: function (response) { + displayMessage("Invalid filter data.", "errorMessage"); + } + }); + } +} + /** * After the user executes a filter than we have to replace the select with the old option with diff --git a/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html b/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html index 81392adb46..5cb175fdad 100644 --- a/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html +++ b/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html @@ -1,5 +1,6 @@

- {% for option in field.options() %} {% endfor %} @@ -21,6 +22,22 @@ {% endfor %} + + {% set runtime_filters = field.get_runtime_filters %} + + {% if runtime_filters %} + {% for key, value in runtime_filters.items() %} +

+ + {% for idx in range(value.fields | length) %} + + + + + {% endfor %} +
+ {% endfor %} + {% endif %} {% endwith %} {% endif %} From c3d94cf007b4fd887ba650f8e7e3dabe2aaa5c3a Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 13 Jan 2021 19:30:01 +0200 Subject: [PATCH 2/9] TVB-2757: Improved solution a little bit. --- .../visualizers/annotations_viewer.py | 5 +++++ .../tvb/adapters/visualizers/connectivity.py | 12 +++++------- .../tvb/core/services/algorithm_service.py | 1 + .../web/controllers/flow_controller.py | 9 +++++++++ .../form_fields/datatype_select_field.html | 19 +++++++------------ 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py index bc976d9117..756fc73be1 100644 --- a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py +++ b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py @@ -37,6 +37,7 @@ from tvb.adapters.visualizers.surface_view import ABCSurfaceDisplayer, SurfaceURLGenerator from tvb.adapters.datatypes.db.region_mapping import RegionMappingIndex from tvb.adapters.datatypes.db.annotation import * +from tvb.core.entities.filters.chain import FilterChain from tvb.core.neocom import h5 from tvb.core.adapters.abcadapter import ABCAdapterForm from tvb.core.adapters.abcdisplayer import URLGenerator @@ -76,6 +77,10 @@ def __init__(self): # Used for filtering self.connectivity_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.connectivity_index, 'connectivity_index') + + annotations_index_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], + values=['fk_connectivity_gid']) + self.annotations_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.annotations_index, 'annotations_index', conditions=self.get_filters()) self.region_mapping_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.region_mapping_index, diff --git a/framework_tvb/tvb/adapters/visualizers/connectivity.py b/framework_tvb/tvb/adapters/visualizers/connectivity.py index 3c83da6852..44f889c50c 100644 --- a/framework_tvb/tvb/adapters/visualizers/connectivity.py +++ b/framework_tvb/tvb/adapters/visualizers/connectivity.py @@ -123,14 +123,17 @@ def __init__(self): self.step = FloatField(ConnectivityViewerModel.step, name='step') + runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], + values=['']) + colors_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.colors = TraitDataTypeSelectField(ConnectivityViewerModel.colors, name='colors', conditions=colors_conditions, - runtime_conditions=self.get_runtime_filters()) + runtime_conditions=runtime_condition) rays_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=rays_conditions, - runtime_conditions=self.get_runtime_filters()) + runtime_conditions=runtime_condition) @staticmethod def get_view_model(): @@ -144,11 +147,6 @@ def get_required_datatype(): def get_filters(): return None - @staticmethod - def get_runtime_filters(): - return {'input_data': FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], - values=[ConnectivityIndex])} - @staticmethod def get_input_name(): return "input_data" diff --git a/framework_tvb/tvb/core/services/algorithm_service.py b/framework_tvb/tvb/core/services/algorithm_service.py index a4786543d6..b7a75e3ff8 100644 --- a/framework_tvb/tvb/core/services/algorithm_service.py +++ b/framework_tvb/tvb/core/services/algorithm_service.py @@ -112,6 +112,7 @@ def fill_selectfield_with_datatypes(self, field, project_id, extra_conditions=No filtering_conditions = FilterChain() filtering_conditions += field.conditions filtering_conditions += extra_conditions + filtering_conditions += field.runtime_conditions datatypes, _ = dao.get_values_of_datatype(project_id, field.datatype_index, filtering_conditions) datatype_options = [] for datatype in datatypes: diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index 218b6d3f9d..2a51eedc91 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -304,6 +304,7 @@ def get_runtime_filtered_form(self, algorithm_id, filters): for key, value in filter_dict.items(): select_field_attr = getattr(form, key) + for i in range(len(value['fields'])): if value['isRuntimeFilter'][i]: runtime_fields.append(value['fields'][i]) @@ -322,6 +323,14 @@ def get_runtime_filtered_form(self, algorithm_id, filters): select_field_attr.runtime_conditions = runtime_conditions self.algorithm_service.fill_selectfield_with_datatypes(select_field_attr, project_id) + fields = [] + operations = [] + values = [] + + runtime_fields = [] + runtime_operations = [] + runtime_values = [] + return {'adapter_form': form} def execute_post(self, project_id, submit_url, step_key, algorithm, **data): diff --git a/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html b/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html index 5cb175fdad..c788c9d655 100644 --- a/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html +++ b/framework_tvb/tvb/interfaces/web/templates/jinja2/form_fields/datatype_select_field.html @@ -25,19 +25,14 @@ {% set runtime_filters = field.get_runtime_filters %} - {% if runtime_filters %} - {% for key, value in runtime_filters.items() %} -
- - {% for idx in range(value.fields | length) %} + {% for idx in range(runtime_filters.fields | length) %} +
- - - - {% endfor %} -
- {% endfor %} - {% endif %} + + + +
+ {% endfor %} {% endwith %} {% endif %} From 9bfe8f29dcb426a589abe89de196bff744620c49 Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 20 Jan 2021 10:00:13 +0200 Subject: [PATCH 3/9] TVB-2757: Separated the 3 types of filters (default, user defined and runtime) and made it possible to apply them at the same time --- .../visualizers/annotations_viewer.py | 19 ++- .../tvb/adapters/visualizers/connectivity.py | 6 +- .../tvb/core/entities/filters/chain.py | 2 + .../tvb/core/services/algorithm_service.py | 5 +- .../web/controllers/flow_controller.py | 78 ++++++------ .../tvb/interfaces/web/static/js/filters.js | 115 +++++++++++++----- .../form_fields/datatype_select_field.html | 24 ++-- 7 files changed, 161 insertions(+), 88 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py index 756fc73be1..38de2c4392 100644 --- a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py +++ b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py @@ -75,16 +75,23 @@ class ConnectivityAnnotationsViewForm(ABCAdapterForm): def __init__(self): super(ConnectivityAnnotationsViewForm, self).__init__() # Used for filtering - self.connectivity_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.connectivity_index, - 'connectivity_index') + annotations_index_filter = FilterChain(fields=[FilterChain.datatype + '.number_of_regions'], operations=["=="], + values=['fk_connectivity_gid']) - annotations_index_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], - values=['fk_connectivity_gid']) + self.connectivity_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.connectivity_index, + 'connectivity_index', + runtime_conditions=('annotations_index', + annotations_index_filter)) self.annotations_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.annotations_index, 'annotations_index', conditions=self.get_filters()) - self.region_mapping_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.region_mapping_index, - 'region_mapping_index') + + rm_runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], + values=[FilterChain.DEFAULT_RUNTIME_VALUE]) + + self.region_mapping_index = TraitDataTypeSelectField( + ConnectivityAnnotationsViewModel.region_mapping_index, 'region_mapping_index', + runtime_conditions=('connectivity_index', rm_runtime_condition)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/adapters/visualizers/connectivity.py b/framework_tvb/tvb/adapters/visualizers/connectivity.py index 44f889c50c..fa16934aae 100644 --- a/framework_tvb/tvb/adapters/visualizers/connectivity.py +++ b/framework_tvb/tvb/adapters/visualizers/connectivity.py @@ -124,16 +124,16 @@ def __init__(self): self.step = FloatField(ConnectivityViewerModel.step, name='step') runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], - values=['']) + values=[FilterChain.DEFAULT_RUNTIME_VALUE]) colors_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.colors = TraitDataTypeSelectField(ConnectivityViewerModel.colors, name='colors', conditions=colors_conditions, - runtime_conditions=runtime_condition) + runtime_conditions=('connectivity_data', runtime_condition)) rays_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=rays_conditions, - runtime_conditions=runtime_condition) + runtime_conditions=('connectivity_data', runtime_condition)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/core/entities/filters/chain.py b/framework_tvb/tvb/core/entities/filters/chain.py index 40ad2ae920..554ae46838 100644 --- a/framework_tvb/tvb/core/entities/filters/chain.py +++ b/framework_tvb/tvb/core/entities/filters/chain.py @@ -77,6 +77,8 @@ class FilterChain(object): algorithm_category_replacement = "AlgorithmCategory" operation_replacement = "Operation" + DEFAULT_RUNTIME_VALUE = "default_runtime_value" + def __init__(self, display_name="", fields=None, values=None, operations=None, operator_between_fields='and'): """ Initialize filter attributes. diff --git a/framework_tvb/tvb/core/services/algorithm_service.py b/framework_tvb/tvb/core/services/algorithm_service.py index b7a75e3ff8..d0ac728f5d 100644 --- a/framework_tvb/tvb/core/services/algorithm_service.py +++ b/framework_tvb/tvb/core/services/algorithm_service.py @@ -112,7 +112,10 @@ def fill_selectfield_with_datatypes(self, field, project_id, extra_conditions=No filtering_conditions = FilterChain() filtering_conditions += field.conditions filtering_conditions += extra_conditions - filtering_conditions += field.runtime_conditions + + if field.runtime_conditions is not None: + filtering_conditions += field.runtime_conditions[1] + datatypes, _ = dao.get_values_of_datatype(project_id, field.datatype_index, filtering_conditions) datatype_options = [] for datatype in datatypes: diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index 2a51eedc91..e23ce864a9 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -286,50 +286,59 @@ def get_filtered_datatypes(self, dt_module, dt_class, filters, has_all_option, h @expose_fragment('form_fields/form') @settings @context_selected - def get_runtime_filtered_form(self, algorithm_id, filters): + def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, runtime_filters): algorithm = dao.get_algorithm_by_id(algorithm_id) adapter = getattr(sys.modules[algorithm.module], algorithm.classname)() form = adapter.get_form_class()() - filter_dict = json.loads(filters) + default_filter_dict = json.loads(default_filters) + user_filter_dict = json.loads(user_filters) + runtime_filter_dict = json.loads(runtime_filters) project_id = common.get_current_project().id - fields = [] - operations = [] - values = [] - - runtime_fields = [] - runtime_operations = [] - runtime_values = [] - - for key, value in filter_dict.items(): + for key, default_filters in default_filter_dict.items(): select_field_attr = getattr(form, key) - for i in range(len(value['fields'])): - if value['isRuntimeFilter'][i]: - runtime_fields.append(value['fields'][i]) - runtime_operations.append(value['operations'][i]) - runtime_values.append(value['values'][i]) - else: - fields.append(value['fields'][i]) - operations.append(value['operations'][i]) - values.append(value['values'][i]) - - conditions = FilterChain(fields=fields, operations=operations, values=values) - select_field_attr.conditions = conditions + default_filter_chain = None + if len(default_filters['default_fields']) > 0: + default_filter_chain = FilterChain(fields=default_filters['default_fields'], + operations=default_filters['default_operations'], + values=default_filters['default_values']) + select_field_attr.conditions = default_filter_chain + + user_filters = user_filter_dict[key] + if len(user_filters['user_fields']) > 0: + select_field_attr.conditions += FilterChain(fields=user_filters['user_fields'], + operations=user_filters['user_operations'], + values=user_filters['user_values']) + runtime_filters = runtime_filter_dict[key] + runtime_filter_values_copy = runtime_filters['runtime_values'].copy() + if len(runtime_filters['runtime_fields']) > 0: + + for i in range(len(runtime_filters['runtime_fields'])): + if(len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: + datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) + if datatype_index: + linked_datatype_field = runtime_filters['runtime_values'][i] + linked_datatype_gid = getattr(datatype_index, linked_datatype_field) + linked_datatype_index = dao.get_datatype_by_gid(linked_datatype_gid) + filter_field = runtime_filters['runtime_fields'][i].replace(FilterChain.datatype + '.', '') + filter_value = getattr(linked_datatype_index, filter_field) + runtime_filters['runtime_values'][i] = filter_value + else: + runtime_filter_values_copy[i] = FilterChain.DEFAULT_RUNTIME_VALUE + + select_field_attr.runtime_conditions = ( + select_field_attr.runtime_conditions[0], FilterChain( + fields=runtime_filters['runtime_fields'], operations=runtime_filters['runtime_operations'], + values=runtime_filters['runtime_values'])) - runtime_conditions = FilterChain(fields=runtime_fields, operations=runtime_operations, - values=runtime_values) - select_field_attr.runtime_conditions = runtime_conditions self.algorithm_service.fill_selectfield_with_datatypes(select_field_attr, project_id) + select_field_attr.data = runtime_filters['ui_value'] + select_field_attr.conditions = default_filter_chain - fields = [] - operations = [] - values = [] - - runtime_fields = [] - runtime_operations = [] - runtime_values = [] + if select_field_attr.runtime_conditions: + select_field_attr.runtime_conditions[1].values = runtime_filter_values_copy return {'adapter_form': form} @@ -354,7 +363,6 @@ def execute_post(self, project_id, submit_url, step_key, algorithm, **data): 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): @@ -368,7 +376,7 @@ def execute_post(self, project_id, submit_url, step_key, algorithm, **data): return {} result = self.operation_services.fire_operation(adapter_instance, common.get_logged_user(), - project_id, view_model=view_model) + project_id, view_model=view_model) if isinstance(result, list): result = "Launched %s operations." % len(result) common.set_important_message(str(result)) diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index 8556a498dc..30cc60fa74 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -52,7 +52,7 @@ function _FIL_createUiForFilterType(filter, newDiv, isDate){ function addFilter(div_id, filters) { //Create a new div for the filter - var newDiv = $('
'); + var newDiv = $('
'); $('#' + div_id).append(newDiv); //This will be the select row to filter by @@ -77,17 +77,17 @@ function addFilter(div_id, filters) { } // recreate them _FIL_createUiForFilterType(filters[this.value], newDiv, filters[this.value].type === 'date'); - }); + }) } + + /** gather all the data from the filters */ function _FIL_gatherData(divId, uiValue){ var children = $('#'+divId).children('div'); - var fields = []; - var operations = []; - var values = []; - var isRuntimeFilter = []; - var triggersFiltering = false; + var default_fields = [], default_operations = [], default_values = []; + var user_fields = [], user_operations = [], user_values = []; + var runtime_fields = [], runtime_operations = [], runtime_values = [], runtime_reverse_filtering_values = []; for (var i = 0; i < children.length; i++) { var elem = children[i].children; @@ -95,32 +95,48 @@ function _FIL_gatherData(divId, uiValue){ if (elem[3].value.trim().length > 0) { var value = elem[3].value.trim(); - if(children[i].className.endsWith('runtime_trigger')){ - value = uiValue; - triggersFiltering = true; - isRuntimeFilter.push(true); - }else{ - isRuntimeFilter.push(false); + if (children[i].className === "user_trigger") { + user_fields.push(elem[1].value); + user_operations.push(elem[2].value); + user_values.push(value); + } else { + if (children[i].className.endsWith('runtime_trigger')) { + let value_from_field = $('#' + children[i].className.replace('_runtime_trigger', '')).val(); + if(value === "default_runtime_value"){ + if (!uiValue) { + value = value_from_field; + } else { + value = uiValue; + } + runtime_reverse_filtering_values.push(''); + }else{ + runtime_reverse_filtering_values.push(value_from_field); + } + + runtime_fields.push(elem[1].value); + runtime_operations.push(elem[2].value); + runtime_values.push(value); + } else { + default_fields.push(elem[1].value); + default_operations.push(elem[2].value); + default_values.push(value); + } } - - fields.push(elem[1].value); - operations.push(elem[2].value); - values.push(value); displayMessage("Filters processed"); - } else { + } + else { displayMessage("Please set a value for all the filters.", "errorMessage"); return; } } - if (fields.length === 0 && operations.length === 0 && values.length === 0 && triggersFiltering === false) { - displayMessage("Cleared filters"); - } - return { filters: {fields: fields, operations: operations, values: values, isRuntimeFilter: isRuntimeFilter}, - triggersFiltering: triggersFiltering}; + return {default_filters: {default_fields: default_fields, default_operations: default_operations, default_values: + default_values}, user_filters: {user_fields: user_fields, user_operations: user_operations, user_values: + user_values}, runtime_filters: {runtime_fields: runtime_fields, runtime_operations: runtime_operations, + runtime_values: runtime_values, runtime_reverse_filtering_values: runtime_reverse_filtering_values}}; } -function applyFilters(datatypeIndex, divId, name, gatheredData) { +function applyUserFilters(datatypeIndex, divId, name, gatheredData) { if (!gatheredData) { //gather all the data from the filters and make an //ajax request to get new data @@ -155,7 +171,7 @@ function applyFilters(datatypeIndex, divId, name, gatheredData) { //Make a request to get new data doAjaxCall({ type: 'POST', - url: "/flow/get_filtered_datatypes/" + dt_module + '/' + dt_class + '/' + $.toJSON(gatheredData) + '/' + + url: "/flow/get_filtered_datatypes/" + dt_module + '/' + dt_class + '/' + $.toJSON(gatheredData.filters) + '/' + has_all_option + '/' + has_none_option, success: function (response) { if (!response) { @@ -175,30 +191,63 @@ function applyFilters(datatypeIndex, divId, name, gatheredData) { }); } -function applyRuntimeFilters(name, selected_value){ +function applyRuntimeFilters(name, selected_value, dynamic_filters){ + + if($('.' + name + '_runtime_trigger').length === 0){ + return; + } + var form = $('#' + name).closest('form'); let form_action = form[0].action; let algorithm_id_start = form_action.lastIndexOf('/'); - let algorthimm_id = form_action.substring(algorithm_id_start + 1, form_action.length); + let algorithm_id = form_action.substring(algorithm_id_start + 1, form_action.length); + + algorithm_id_start = algorithm_id.lastIndexOf('?'); + if(algorithm_id_start!==-1){ + algorithm_id = algorithm_id.substring(0, algorithm_id_start) + } let select_fields = form.find('select.dataset-selector'); - var fields_and_filters = {} + var fields_and_default_filters = {}; + var fields_and_user_filters = {}; + var fields_and_runtime_filters = {}; - let filters; - for (var i = 0; i < select_fields.length; i++) { + var is_runtime_filtering = false; + let filter_values; + for (let i = 0; i < select_fields.length; i++) { filter_values = _FIL_gatherData(select_fields[i].id + 'data_select', selected_value); - fields_and_filters[select_fields[i].id] = filter_values.filters; + filter_values.runtime_filters['ui_value'] = select_fields[i].value; + fields_and_default_filters[select_fields[i].id] = filter_values.default_filters; + fields_and_user_filters[select_fields[i].id] = filter_values.user_filters; + fields_and_runtime_filters[select_fields[i].id] = filter_values.runtime_filters; + + if(filter_values.runtime_filters['runtime_fields'].length > 0){ + is_runtime_filtering = true; + } } - if(filter_values.triggersFiltering) { + if(is_runtime_filtering) { doAjaxCall({ type: 'POST', - url: "/flow/get_runtime_filtered_form/" + algorthimm_id + '/' + $.toJSON(fields_and_filters), + url: "/flow/get_runtime_filtered_form/" + algorithm_id + '/' + $.toJSON(fields_and_default_filters) + + '/' + $.toJSON(fields_and_user_filters) + '/' + $.toJSON(fields_and_runtime_filters), success: function (response) { const t = document.createRange().createContextualFragment(response); let adapters_div = $('.adaptersDiv'); adapters_div.children('fieldset').replaceWith(t); + + for(var key in fields_and_user_filters){ + const divId = key + 'data_select'; + for(var i=0; i {% if field.draw_dynamic_conditions_buttons %}
- - + + {% with %} {% set form_filters = field.get_form_filters %} @@ -25,14 +27,16 @@ {% set runtime_filters = field.get_runtime_filters %} - {% for idx in range(runtime_filters.fields | length) %} -
+ {% if runtime_filters %} + {% for idx in range(runtime_filters[1].fields | length) %} +
- - - -
- {% endfor %} + + + +
+ {% endfor %} + {% endif %} {% endwith %}
{% endif %} From 516255537044686e1fa0df4928e72321e8fe0c7f Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 20 Jan 2021 10:45:35 +0200 Subject: [PATCH 4/9] TVB-2757: Edit get_filtered_datatypes to make it compatible with the changes. --- .../web/controllers/flow_controller.py | 32 ++++++++++--------- .../tvb/interfaces/web/static/js/filters.js | 5 +-- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index e23ce864a9..68535a9663 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -254,30 +254,32 @@ def default(self, step_key, adapter_key, cancel=False, back_page=None, **data): @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): + def get_filtered_datatypes(self, dt_module, dt_class, default_filters, user_filters, runtime_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) + default_filters_dict = json.loads(default_filters) + user_filters_dict = json.loads(user_filters) + runtime_filters_dict = json.loads(runtime_filters) + + filters = FilterChain(fields=default_filters_dict['default_fields'], + operations=default_filters_dict['default_operations'], + values=default_filters_dict['default_values']) + filters += FilterChain(fields=user_filters_dict['user_fields'], + operations=user_filters_dict['user_operations'], + values=user_filters_dict['user_values']) + filters += FilterChain(fields=runtime_filters_dict['runtime_fields'], + operations=runtime_filters_dict['runtime_operations'], + values=runtime_filters_dict['runtime_values']) project = common.get_current_project() 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, conditions=filter, + select_field = TraitDataTypeSelectField(data_type_gid_attr, conditions=filters, has_all_option=string2bool(has_all_option)) self.algorithm_service.fill_selectfield_with_datatypes(select_field, project.id) @@ -316,7 +318,7 @@ def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, if len(runtime_filters['runtime_fields']) > 0: for i in range(len(runtime_filters['runtime_fields'])): - if(len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: + if (len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) if datatype_index: linked_datatype_field = runtime_filters['runtime_values'][i] diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index 30cc60fa74..85dfb5b9d4 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -171,8 +171,9 @@ function applyUserFilters(datatypeIndex, divId, name, gatheredData) { //Make a request to get new data doAjaxCall({ type: 'POST', - url: "/flow/get_filtered_datatypes/" + dt_module + '/' + dt_class + '/' + $.toJSON(gatheredData.filters) + '/' + - has_all_option + '/' + has_none_option, + url: "/flow/get_filtered_datatypes/" + dt_module + '/' + dt_class + '/' + + $.toJSON(gatheredData.default_filters) + '/' + $.toJSON(gatheredData.user_filters) + + '/' + $.toJSON(gatheredData.runtime_filters) + '/' + has_all_option + '/' + has_none_option, success: function (response) { if (!response) { displayMessage(`No results for the ${name} filtering!`, "warningMessage"); From b2e3ccbae0b6ec13fb52e3ee90f305b1fa2bc6f0 Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 20 Jan 2021 13:49:09 +0200 Subject: [PATCH 5/9] TVB-2757: Fill reversed filter values when applying user defined filters as well --- .../web/controllers/flow_controller.py | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index 68535a9663..17ace651b4 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -251,6 +251,17 @@ def default(self, step_key, adapter_key, cancel=False, back_page=None, **data): self.fill_default_attributes(template_specification, algorithm.displayname) return template_specification + @staticmethod + def _fill_reversed_filter_value(runtime_filters, i): + datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) + if datatype_index: + linked_datatype_field = runtime_filters['runtime_values'][i] + linked_datatype_gid = getattr(datatype_index, linked_datatype_field) + linked_datatype_index = dao.get_datatype_by_gid(linked_datatype_gid) + filter_field = runtime_filters['runtime_fields'][i].replace(FilterChain.datatype + '.', '') + filter_value = getattr(linked_datatype_index, filter_field) + runtime_filters['runtime_values'][i] = filter_value + @expose_fragment('form_fields/options_field') @settings @context_selected @@ -265,6 +276,10 @@ def get_filtered_datatypes(self, dt_module, dt_class, default_filters, user_filt user_filters_dict = json.loads(user_filters) runtime_filters_dict = json.loads(runtime_filters) + for i in range(len(runtime_filters_dict['runtime_fields'])): + if (len(runtime_filters_dict['runtime_reverse_filtering_values'][i])) > 0: + self._fill_reversed_filter_value(runtime_filters_dict, i) + filters = FilterChain(fields=default_filters_dict['default_fields'], operations=default_filters_dict['default_operations'], values=default_filters_dict['default_values']) @@ -301,37 +316,26 @@ def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, for key, default_filters in default_filter_dict.items(): select_field_attr = getattr(form, key) - default_filter_chain = None - if len(default_filters['default_fields']) > 0: - default_filter_chain = FilterChain(fields=default_filters['default_fields'], - operations=default_filters['default_operations'], - values=default_filters['default_values']) - select_field_attr.conditions = default_filter_chain + default_filter_chain = FilterChain(fields=default_filters['default_fields'], + operations=default_filters['default_operations'], + values=default_filters['default_values']) + select_field_attr.conditions = default_filter_chain user_filters = user_filter_dict[key] - if len(user_filters['user_fields']) > 0: - select_field_attr.conditions += FilterChain(fields=user_filters['user_fields'], - operations=user_filters['user_operations'], - values=user_filters['user_values']) + select_field_attr.conditions += FilterChain(fields=user_filters['user_fields'], + operations=user_filters['user_operations'], + values=user_filters['user_values']) runtime_filters = runtime_filter_dict[key] runtime_filter_values_copy = runtime_filters['runtime_values'].copy() - if len(runtime_filters['runtime_fields']) > 0: - - for i in range(len(runtime_filters['runtime_fields'])): - if (len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: - datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) - if datatype_index: - linked_datatype_field = runtime_filters['runtime_values'][i] - linked_datatype_gid = getattr(datatype_index, linked_datatype_field) - linked_datatype_index = dao.get_datatype_by_gid(linked_datatype_gid) - filter_field = runtime_filters['runtime_fields'][i].replace(FilterChain.datatype + '.', '') - filter_value = getattr(linked_datatype_index, filter_field) - runtime_filters['runtime_values'][i] = filter_value - else: - runtime_filter_values_copy[i] = FilterChain.DEFAULT_RUNTIME_VALUE - - select_field_attr.runtime_conditions = ( - select_field_attr.runtime_conditions[0], FilterChain( + + for i in range(len(runtime_filters['runtime_fields'])): + if (len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: + self._fill_reversed_filter_value(runtime_filters, i) + else: + runtime_filter_values_copy[i] = FilterChain.DEFAULT_RUNTIME_VALUE + + if select_field_attr.runtime_conditions: + select_field_attr.runtime_conditions = (select_field_attr.runtime_conditions[0], FilterChain( fields=runtime_filters['runtime_fields'], operations=runtime_filters['runtime_operations'], values=runtime_filters['runtime_values'])) From dd6e5ac80586bea7f67f71ace5b3f182a7a252d4 Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Wed, 20 Jan 2021 14:44:32 +0200 Subject: [PATCH 6/9] TVB-2757: Remove unused JS functions. --- .../tvb/interfaces/web/static/js/filters.js | 68 +------------------ 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index 85dfb5b9d4..60d2e8c2ec 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -255,70 +255,4 @@ function applyRuntimeFilters(name, selected_value, dynamic_filters){ } }); } -} - - -/** - * After the user executes a filter than we have to replace the select with the old option with - * the select which contains only the options that satisfies the filters. - * - * @param parentDiv the parent div in which is located the select - * @param newSelect the html that contains the new select - * @param selectName the name of the old select - */ -function replaceSelect(parentDiv, newSelect, selectName) { - var allChildren = parentDiv.children; - for (var j = 0; j < allChildren.length; j++) { - if (allChildren[j].nodeName == 'SELECT' && allChildren[j].name == selectName) { - $(newSelect).insertAfter($(allChildren[j])); - parentDiv.removeChild(allChildren[j]); - break; - } - } -} - -/** - * Filter fields which are linked with current entity. - * @param {list} linkedDataList list of lists. - * @param {string} currentSelectedGID for current input - * @param {string} treeSessionKey Key - */ -function filterLinked(linkedDataList, currentSelectedGID, treeSessionKey) { - if (currentSelectedGID.length < 1) { - return; - } - for (var i = 0; i < linkedDataList.length; i++) { - var linkedData = linkedDataList[i]; - var elemName = linkedData.linked_elem_name; - - var filterField = linkedData.linked_elem_field; - var filterData = { - 'fields': [filterField], - 'operations': ["in"], - 'values': [currentSelectedGID.split(' ')] - }; - - if (!linkedData.linked_elem_parent_name && !linkedData.linked_elem_parent_option) { - applyFilters("", elemName + 'data_select', elemName, treeSessionKey, filterData); - } - - var linkedInputName = linkedData.linked_elem_parent_name + "_parameters_option_"; - var parentDivID = 'data_' + linkedData.linked_elem_parent_name; - - if (linkedData.linked_elem_parent_option) { - linkedInputName = linkedInputName + linkedData.linked_elem_parent_option + "_" + elemName; - parentDivID += linkedData.linked_elem_parent_option; - applyFilters(parentDivID, linkedInputName + 'data_select', linkedInputName, treeSessionKey, filterData); - } else { - $("select[id^='" + linkedInputName + "']").each(function () { - if ($(this)[0].id.indexOf("_" + elemName) < 0) { - return; - } - var option_name = $(this)[0].id.replace("_" + elemName, '').replace(linkedInputName, ''); - linkedInputName = $(this)[0].id; - parentDivID += option_name; // todo : possible bug. option names will be concatenated many times if this each runs more than once - applyFilters(parentDivID, linkedInputName + 'data_select', linkedInputName, treeSessionKey, filterData); - }); - } - } -} +} \ No newline at end of file From cc87b1c871e0f6e3729d83afc6c85048e7b4147f Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Fri, 22 Jan 2021 10:07:20 +0200 Subject: [PATCH 7/9] TVB-2757: Add correctly dynamic filters for each field when rebuilding the user defined filters + add runtime filters for Surface Viewer, RM Viewer and TSVolume Viewer --- .../tvb/adapters/visualizers/annotations_viewer.py | 6 +++--- .../tvb/adapters/visualizers/surface_view.py | 14 ++++++++++++-- .../tvb/adapters/visualizers/time_series_volume.py | 6 +++++- .../interfaces/web/controllers/flow_controller.py | 10 ++++++++-- .../tvb/interfaces/web/static/js/filters.js | 11 ++++------- .../jinja2/form_fields/datatype_select_field.html | 2 +- .../web/templates/jinja2/form_fields/form.html | 1 + 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py index 38de2c4392..682beb0476 100644 --- a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py +++ b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py @@ -75,13 +75,13 @@ class ConnectivityAnnotationsViewForm(ABCAdapterForm): def __init__(self): super(ConnectivityAnnotationsViewForm, self).__init__() # Used for filtering - annotations_index_filter = FilterChain(fields=[FilterChain.datatype + '.number_of_regions'], operations=["=="], - values=['fk_connectivity_gid']) + connectivity_index_filter = FilterChain(fields=[FilterChain.datatype + '.number_of_regions'], operations=["=="], + values=['fk_connectivity_gid']) self.connectivity_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.connectivity_index, 'connectivity_index', runtime_conditions=('annotations_index', - annotations_index_filter)) + connectivity_index_filter)) self.annotations_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.annotations_index, 'annotations_index', conditions=self.get_filters()) diff --git a/framework_tvb/tvb/adapters/visualizers/surface_view.py b/framework_tvb/tvb/adapters/visualizers/surface_view.py index 640eda8122..1da22ec20e 100644 --- a/framework_tvb/tvb/adapters/visualizers/surface_view.py +++ b/framework_tvb/tvb/adapters/visualizers/surface_view.py @@ -159,12 +159,18 @@ class BaseSurfaceViewerForm(ABCAdapterForm): def __init__(self): super(BaseSurfaceViewerForm, self).__init__() + self.region_map = TraitDataTypeSelectField(BaseSurfaceViewerModel.region_map, name='region_map') + conn_filter = FilterChain( fields=[FilterChain.datatype + '.ndim', FilterChain.datatype + '.has_surface_mapping'], - operations=["==", "=="], values=[1, True]) + operations=["==", "=="], values=[1, "1"]) + cm_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], + values=['fk_connectivity_gid:fk_connectivity_gid']) self.connectivity_measure = TraitDataTypeSelectField(BaseSurfaceViewerModel.connectivity_measure, - name='connectivity_measure', conditions=conn_filter) + name='connectivity_measure', conditions=conn_filter, + runtime_conditions=('region_map', cm_runtime_filter)) + self.shell_surface = TraitDataTypeSelectField(BaseSurfaceViewerModel.shell_surface, name='shell_surface') @staticmethod @@ -184,6 +190,10 @@ class SurfaceViewerModel(BaseSurfaceViewerModel): class SurfaceViewerForm(BaseSurfaceViewerForm): def __init__(self): super(SurfaceViewerForm, self).__init__() + rm_runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_surface_gid'], operations=["=="], + values=[FilterChain.DEFAULT_RUNTIME_VALUE]) + self.region_map.runtime_conditions = ('surface', rm_runtime_condition) + self.surface = TraitDataTypeSelectField(SurfaceViewerModel.surface, name='surface') @staticmethod diff --git a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py index dec8124928..e3785acfd2 100644 --- a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py +++ b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py @@ -76,7 +76,11 @@ def __init__(self): super(TimeSeriesVolumeVisualiserForm, self).__init__() self.time_series = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.time_series, name='time_series', conditions=self.get_filters()) - self.background = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.background, name='background') + + volume_index_filter = FilterChain(fields=[FilterChain.datatype + '.voxel_size'], operations=["=="], + values=['fk_volume_gid']) + self.background = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.background, name='background', + runtime_conditions=('time_series', volume_index_filter)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index 17ace651b4..eb096cb714 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -256,11 +256,16 @@ def _fill_reversed_filter_value(runtime_filters, i): datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) if datatype_index: linked_datatype_field = runtime_filters['runtime_values'][i] - linked_datatype_gid = getattr(datatype_index, linked_datatype_field) + split_linked_datatype_field = linked_datatype_field.split(':') + linked_datatype_gid = getattr(datatype_index, split_linked_datatype_field[0]) linked_datatype_index = dao.get_datatype_by_gid(linked_datatype_gid) filter_field = runtime_filters['runtime_fields'][i].replace(FilterChain.datatype + '.', '') + filter_value = getattr(linked_datatype_index, filter_field) runtime_filters['runtime_values'][i] = filter_value + runtime_filters['runtime_fields'][i] = FilterChain.datatype + '.' + (split_linked_datatype_field[1] if + len(split_linked_datatype_field) > 1 + else filter_field) @expose_fragment('form_fields/options_field') @settings @@ -327,7 +332,7 @@ def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, values=user_filters['user_values']) runtime_filters = runtime_filter_dict[key] runtime_filter_values_copy = runtime_filters['runtime_values'].copy() - + runtime_filter_fields_copy = runtime_filters['runtime_fields'].copy() for i in range(len(runtime_filters['runtime_fields'])): if (len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: self._fill_reversed_filter_value(runtime_filters, i) @@ -345,6 +350,7 @@ def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, if select_field_attr.runtime_conditions: select_field_attr.runtime_conditions[1].values = runtime_filter_values_copy + select_field_attr.runtime_conditions[1].fields = runtime_filter_fields_copy return {'adapter_form': form} diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index 60d2e8c2ec..be85759036 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -103,11 +103,7 @@ function _FIL_gatherData(divId, uiValue){ if (children[i].className.endsWith('runtime_trigger')) { let value_from_field = $('#' + children[i].className.replace('_runtime_trigger', '')).val(); if(value === "default_runtime_value"){ - if (!uiValue) { - value = value_from_field; - } else { - value = uiValue; - } + value = value_from_field; runtime_reverse_filtering_values.push(''); }else{ runtime_reverse_filtering_values.push(value_from_field); @@ -192,7 +188,7 @@ function applyUserFilters(datatypeIndex, divId, name, gatheredData) { }); } -function applyRuntimeFilters(name, selected_value, dynamic_filters){ +function applyRuntimeFilters(name, selected_value){ if($('.' + name + '_runtime_trigger').length === 0){ return; @@ -241,7 +237,8 @@ function applyRuntimeFilters(name, selected_value, dynamic_filters){ for(var key in fields_and_user_filters){ const divId = key + 'data_select'; for(var i=0; i {{ ght.generate_help_tooltip(field) }}
From 56b1027df9394bc525cba4ae90222774557063d6 Mon Sep 17 00:00:00 2001 From: "Robert.Vincze" Date: Fri, 22 Jan 2021 13:56:39 +0200 Subject: [PATCH 8/9] TVB-2757: Review and add comments. --- .../visualizers/annotations_viewer.py | 5 +- .../tvb/adapters/visualizers/connectivity.py | 18 ++--- .../tvb/adapters/visualizers/surface_view.py | 2 +- .../visualizers/time_series_volume.py | 6 +- .../tvb/core/entities/filters/chain.py | 1 + .../web/controllers/flow_controller.py | 65 ++++++++++++---- .../tvb/interfaces/web/static/js/filters.js | 74 ++++++++++--------- 7 files changed, 108 insertions(+), 63 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py index 682beb0476..f97c961ca8 100644 --- a/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py +++ b/framework_tvb/tvb/adapters/visualizers/annotations_viewer.py @@ -33,6 +33,7 @@ """ import json + from tvb.adapters.datatypes.h5.surface_h5 import SurfaceH5 from tvb.adapters.visualizers.surface_view import ABCSurfaceDisplayer, SurfaceURLGenerator from tvb.adapters.datatypes.db.region_mapping import RegionMappingIndex @@ -74,10 +75,9 @@ class ConnectivityAnnotationsViewForm(ABCAdapterForm): def __init__(self): super(ConnectivityAnnotationsViewForm, self).__init__() - # Used for filtering + connectivity_index_filter = FilterChain(fields=[FilterChain.datatype + '.number_of_regions'], operations=["=="], values=['fk_connectivity_gid']) - self.connectivity_index = TraitDataTypeSelectField(ConnectivityAnnotationsViewModel.connectivity_index, 'connectivity_index', runtime_conditions=('annotations_index', @@ -88,7 +88,6 @@ def __init__(self): rm_runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], values=[FilterChain.DEFAULT_RUNTIME_VALUE]) - self.region_mapping_index = TraitDataTypeSelectField( ConnectivityAnnotationsViewModel.region_mapping_index, 'region_mapping_index', runtime_conditions=('connectivity_index', rm_runtime_condition)) diff --git a/framework_tvb/tvb/adapters/visualizers/connectivity.py b/framework_tvb/tvb/adapters/visualizers/connectivity.py index fa16934aae..4df180223e 100644 --- a/framework_tvb/tvb/adapters/visualizers/connectivity.py +++ b/framework_tvb/tvb/adapters/visualizers/connectivity.py @@ -37,6 +37,7 @@ import math import numpy from copy import copy + from tvb.adapters.visualizers.time_series import ABCSpaceDisplayer from tvb.adapters.visualizers.surface_view import SurfaceURLGenerator from tvb.basic.neotraits.api import Attr @@ -116,6 +117,7 @@ def __init__(self): self.connectivity_data = TraitDataTypeSelectField(ConnectivityViewerModel.connectivity, name='connectivity_data', conditions=self.get_filters()) + surface_conditions = FilterChain(fields=[FilterChain.datatype + '.surface_type'], operations=["=="], values=['Cortical Surface']) self.surface_data = TraitDataTypeSelectField(ConnectivityViewerModel.surface_data, name='surface_data', @@ -123,17 +125,15 @@ def __init__(self): self.step = FloatField(ConnectivityViewerModel.step, name='step') - runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], - values=[FilterChain.DEFAULT_RUNTIME_VALUE]) + cm_condition = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) + cm_runtime_condition = FilterChain(fields=[FilterChain.datatype + '.fk_connectivity_gid'], operations=["=="], + values=[FilterChain.DEFAULT_RUNTIME_VALUE]) - colors_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) self.colors = TraitDataTypeSelectField(ConnectivityViewerModel.colors, name='colors', - conditions=colors_conditions, - runtime_conditions=('connectivity_data', runtime_condition)) - - rays_conditions = FilterChain(fields=[FilterChain.datatype + '.ndim'], operations=["=="], values=[1]) - self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=rays_conditions, - runtime_conditions=('connectivity_data', runtime_condition)) + conditions=cm_condition, + runtime_conditions=('connectivity_data', cm_runtime_condition)) + self.rays = TraitDataTypeSelectField(ConnectivityViewerModel.rays, name='rays', conditions=cm_condition, + runtime_conditions=('connectivity_data', cm_runtime_condition)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/adapters/visualizers/surface_view.py b/framework_tvb/tvb/adapters/visualizers/surface_view.py index 1da22ec20e..60f2916c77 100644 --- a/framework_tvb/tvb/adapters/visualizers/surface_view.py +++ b/framework_tvb/tvb/adapters/visualizers/surface_view.py @@ -164,7 +164,7 @@ def __init__(self): conn_filter = FilterChain( fields=[FilterChain.datatype + '.ndim', FilterChain.datatype + '.has_surface_mapping'], - operations=["==", "=="], values=[1, "1"]) + operations=["==", "=="], values=[1, True]) cm_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], values=['fk_connectivity_gid:fk_connectivity_gid']) self.connectivity_measure = TraitDataTypeSelectField(BaseSurfaceViewerModel.connectivity_measure, diff --git a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py index e3785acfd2..c7f4154a8f 100644 --- a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py +++ b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py @@ -77,10 +77,10 @@ def __init__(self): self.time_series = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.time_series, name='time_series', conditions=self.get_filters()) - volume_index_filter = FilterChain(fields=[FilterChain.datatype + '.voxel_size'], operations=["=="], - values=['fk_volume_gid']) + mri_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], + values=['fk_volume_gid:fk_volume_gid']) self.background = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.background, name='background', - runtime_conditions=('time_series', volume_index_filter)) + runtime_conditions=('time_series', mri_runtime_filter)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/core/entities/filters/chain.py b/framework_tvb/tvb/core/entities/filters/chain.py index 554ae46838..07b3deb112 100644 --- a/framework_tvb/tvb/core/entities/filters/chain.py +++ b/framework_tvb/tvb/core/entities/filters/chain.py @@ -77,6 +77,7 @@ class FilterChain(object): algorithm_category_replacement = "AlgorithmCategory" operation_replacement = "Operation" + # This is used for the simplest runtime filters, where we just have to replace the current value at runtime DEFAULT_RUNTIME_VALUE = "default_runtime_value" def __init__(self, display_name="", fields=None, values=None, operations=None, operator_between_fields='and'): diff --git a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py index eb096cb714..48cd8dd454 100644 --- a/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py +++ b/framework_tvb/tvb/interfaces/web/controllers/flow_controller.py @@ -253,15 +253,19 @@ def default(self, step_key, adapter_key, cancel=False, back_page=None, **data): @staticmethod def _fill_reversed_filter_value(runtime_filters, i): + # Get index of the currently chosen value for the field that the filtering will be applied on datatype_index = dao.get_datatype_by_gid(runtime_filters['runtime_reverse_filtering_values'][i]) if datatype_index: + # Get the linked datatype and the value that needs to be used for the filter linked_datatype_field = runtime_filters['runtime_values'][i] split_linked_datatype_field = linked_datatype_field.split(':') linked_datatype_gid = getattr(datatype_index, split_linked_datatype_field[0]) linked_datatype_index = dao.get_datatype_by_gid(linked_datatype_gid) filter_field = runtime_filters['runtime_fields'][i].replace(FilterChain.datatype + '.', '') - filter_value = getattr(linked_datatype_index, filter_field) + + # If there was a ':' character in linked_datatype_field, it means that the linked datatype is not among the + # ui fields of the current form and the value needed to be obtained from one of the existing fields runtime_filters['runtime_values'][i] = filter_value runtime_filters['runtime_fields'][i] = FilterChain.datatype + '.' + (split_linked_datatype_field[1] if len(split_linked_datatype_field) > 1 @@ -272,9 +276,17 @@ def _fill_reversed_filter_value(runtime_filters, i): @context_selected def get_filtered_datatypes(self, dt_module, dt_class, default_filters, user_filters, runtime_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. + # type: (str, str, str, str, str, bool, bool) -> dict + """ + This method applies all three types of filters on one field. + @param dt_module: module of the field's datatype index + @param dt_class: class of the field's datatype index + @param default_filters: a string in json format which contains all default filters for this field + @param user_filters: a string in json format which contains all filters defined by users for this field + @param runtime_filters: a string in json format which contains all filters for this field + that have values that can be obtained only at runtime (related to linked data types) + @param has_all_option: if the All option should be added or not + @param has_none_option: if the None option should be added or not (if the field is required or not) """ index_class = getattr(sys.modules[dt_module], dt_class)() default_filters_dict = json.loads(default_filters) @@ -309,45 +321,72 @@ def get_filtered_datatypes(self, dt_module, dt_class, default_filters, user_filt @settings @context_selected def get_runtime_filtered_form(self, algorithm_id, default_filters, user_filters, runtime_filters): + # type: (str, str, str, str) -> dict + """ + This method returns a newly rendered form, where all the filters are applied on the respective fields. + @param algorithm_id: id of the adapter that can be used to return an instance of the current form + @param default_filters: a string in json format which contains all default filters + @param user_filters: a string in json format which contains all filters defined by users + @param runtime_filters: a string in json format which contains all filters that have values that can be obtained + only at runtime (related to linked data types) + """ + + # Get an instance of the needed form class algorithm = dao.get_algorithm_by_id(algorithm_id) adapter = getattr(sys.modules[algorithm.module], algorithm.classname)() form = adapter.get_form_class()() - default_filter_dict = json.loads(default_filters) - user_filter_dict = json.loads(user_filters) - runtime_filter_dict = json.loads(runtime_filters) + # Load filters as dictionaries + user_filters_dict = json.loads(user_filters) + default_filters_dict = json.loads(default_filters) + runtime_filters_dict = json.loads(runtime_filters) project_id = common.get_current_project().id - for key, default_filters in default_filter_dict.items(): + # Iterate over the filters of each field + for key, user_filters in user_filters_dict.items(): select_field_attr = getattr(form, key) - default_filter_chain = FilterChain(fields=default_filters['default_fields'], - operations=default_filters['default_operations'], - values=default_filters['default_values']) + # Add default filters even if they are empty, otherwise applying the + operator on None will fail + default_filter_chain = FilterChain(fields=default_filters_dict[key]['default_fields'], + operations=default_filters_dict[key]['default_operations'], + values=default_filters_dict[key]['default_values']) select_field_attr.conditions = default_filter_chain - user_filters = user_filter_dict[key] + # Add filters defined by users so they can both be applied select_field_attr.conditions += FilterChain(fields=user_filters['user_fields'], operations=user_filters['user_operations'], values=user_filters['user_values']) - runtime_filters = runtime_filter_dict[key] + runtime_filters = runtime_filters_dict[key] + + # Keep these values because they need to be reset after applying the runtime filters runtime_filter_values_copy = runtime_filters['runtime_values'].copy() runtime_filter_fields_copy = runtime_filters['runtime_fields'].copy() + for i in range(len(runtime_filters['runtime_fields'])): + + # If this condition is true, then it means we need to apply the filters in 'inversed order', + # so we need the information from the filter value (and maybe from the filter field as well) if (len(runtime_filters['runtime_reverse_filtering_values'][i])) > 0: self._fill_reversed_filter_value(runtime_filters, i) else: runtime_filter_values_copy[i] = FilterChain.DEFAULT_RUNTIME_VALUE + # Runtime conditions are added as a tuple of two elements, where the first element is the field that can + # trigger a change in the current field and the second element is the filter itself if select_field_attr.runtime_conditions: select_field_attr.runtime_conditions = (select_field_attr.runtime_conditions[0], FilterChain( fields=runtime_filters['runtime_fields'], operations=runtime_filters['runtime_operations'], values=runtime_filters['runtime_values'])) + # Perform the filtering self.algorithm_service.fill_selectfield_with_datatypes(select_field_attr, project_id) select_field_attr.data = runtime_filters['ui_value'] + + # After applying the user defined filters, we need to eliminate them so they won't be added as hidden + # fields next to the default and runtime filters select_field_attr.conditions = default_filter_chain + # Runtime conditions need to be reset, because they were edited so they can be applied if select_field_attr.runtime_conditions: select_field_attr.runtime_conditions[1].values = runtime_filter_values_copy select_field_attr.runtime_conditions[1].fields = runtime_filter_fields_copy diff --git a/framework_tvb/tvb/interfaces/web/static/js/filters.js b/framework_tvb/tvb/interfaces/web/static/js/filters.js index be85759036..249edead57 100644 --- a/framework_tvb/tvb/interfaces/web/static/js/filters.js +++ b/framework_tvb/tvb/interfaces/web/static/js/filters.js @@ -85,6 +85,8 @@ function addFilter(div_id, filters) { /** gather all the data from the filters */ function _FIL_gatherData(divId, uiValue){ var children = $('#'+divId).children('div'); + /* Keep the three types of filters in separate dicts and an additional list to keep the values of the linked + * datatypes for runtime filters where they are needed */ var default_fields = [], default_operations = [], default_values = []; var user_fields = [], user_operations = [], user_values = []; var runtime_fields = [], runtime_operations = [], runtime_values = [], runtime_reverse_filtering_values = []; @@ -95,6 +97,8 @@ function _FIL_gatherData(divId, uiValue){ if (elem[3].value.trim().length > 0) { var value = elem[3].value.trim(); + // User defined filters and default filters need to be kept separately because user filters need to be + // readded to the UI after rerendering the form if (children[i].className === "user_trigger") { user_fields.push(elem[1].value); user_operations.push(elem[2].value); @@ -106,6 +110,8 @@ function _FIL_gatherData(divId, uiValue){ value = value_from_field; runtime_reverse_filtering_values.push(''); }else{ + /* The field that the filter needs to be applied to does not have a reference to the field that + triggered the change, so we need to apply the field in another way */ runtime_reverse_filtering_values.push(value_from_field); } @@ -160,7 +166,8 @@ function applyUserFilters(datatypeIndex, divId, name, gatheredData) { has_none_option = true; } - if (select_field.options[select_field.options.length - 1] && select_field.options[select_field.options.length - 1].innerHTML === "All"){ + if (select_field.options[select_field.options.length - 1] && + select_field.options[select_field.options.length - 1].innerHTML === "All"){ has_all_option = true; } @@ -194,6 +201,7 @@ function applyRuntimeFilters(name, selected_value){ return; } + // Obtain the form and the algorithm id from it var form = $('#' + name).closest('form'); let form_action = form[0].action; let algorithm_id_start = form_action.lastIndexOf('/'); @@ -209,7 +217,7 @@ function applyRuntimeFilters(name, selected_value){ var fields_and_user_filters = {}; var fields_and_runtime_filters = {}; - var is_runtime_filtering = false; + // Iterate over the fields of the form and gather the filters for each field let filter_values; for (let i = 0; i < select_fields.length; i++) { filter_values = _FIL_gatherData(select_fields[i].id + 'data_select', selected_value); @@ -217,39 +225,37 @@ function applyRuntimeFilters(name, selected_value){ fields_and_default_filters[select_fields[i].id] = filter_values.default_filters; fields_and_user_filters[select_fields[i].id] = filter_values.user_filters; fields_and_runtime_filters[select_fields[i].id] = filter_values.runtime_filters; - - if(filter_values.runtime_filters['runtime_fields'].length > 0){ - is_runtime_filtering = true; - } } - if(is_runtime_filtering) { - doAjaxCall({ - type: 'POST', - url: "/flow/get_runtime_filtered_form/" + algorithm_id + '/' + $.toJSON(fields_and_default_filters) + - '/' + $.toJSON(fields_and_user_filters) + '/' + $.toJSON(fields_and_runtime_filters), - success: function (response) { - const t = document.createRange().createContextualFragment(response); - - let adapters_div = $('.adaptersDiv'); - adapters_div.children('fieldset').replaceWith(t); - - for(var key in fields_and_user_filters){ - const divId = key + 'data_select'; - for(var i=0; i Date: Fri, 22 Jan 2021 16:08:19 +0200 Subject: [PATCH 9/9] TVB-2757: Add filtering of Connectivity Measures based on selected Region Volume Mapping. --- .../adapters/visualizers/region_volume_mapping.py | 12 ++++++++++-- .../tvb/adapters/visualizers/time_series_volume.py | 9 +++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/framework_tvb/tvb/adapters/visualizers/region_volume_mapping.py b/framework_tvb/tvb/adapters/visualizers/region_volume_mapping.py index 51b8c33046..e4951ad5a4 100644 --- a/framework_tvb/tvb/adapters/visualizers/region_volume_mapping.py +++ b/framework_tvb/tvb/adapters/visualizers/region_volume_mapping.py @@ -388,8 +388,12 @@ def __init__(self): self.connectivity_measure = TraitDataTypeSelectField( ConnectivityMeasureVolumeVisualizerModel.connectivity_measure, name='connectivity_measure', conditions=self.get_filters()) + + rvm_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], + values=['fk_connectivity_gid:fk_connectivity_gid']) self.region_mapping_volume = TraitDataTypeSelectField( - ConnectivityMeasureVolumeVisualizerModel.region_mapping_volume, name='region_mapping_volume') + ConnectivityMeasureVolumeVisualizerModel.region_mapping_volume, name='region_mapping_volume', + runtime_conditions=('connectivity_measure', rvm_runtime_filter)) @staticmethod def get_view_model(): @@ -461,8 +465,12 @@ def __init__(self): cm_conditions = FilterChain( fields=[FilterChain.datatype + '.ndim', FilterChain.datatype + '.has_volume_mapping'], operations=["==", "=="], values=[1, True]) + cm_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], + values=['fk_connectivity_gid:fk_connectivity_gid']) self.connectivity_measure = TraitDataTypeSelectField(RegionVolumeMappingVisualiserModel.connectivity_measure, - name='connectivity_measure', conditions=cm_conditions) + name='connectivity_measure', conditions=cm_conditions, + runtime_conditions=('region_mapping_volume', + cm_runtime_filter)) @staticmethod def get_view_model(): diff --git a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py index c7f4154a8f..12333d5c1c 100644 --- a/framework_tvb/tvb/adapters/visualizers/time_series_volume.py +++ b/framework_tvb/tvb/adapters/visualizers/time_series_volume.py @@ -74,13 +74,10 @@ class TimeSeriesVolumeVisualiserForm(ABCAdapterForm): def __init__(self): super(TimeSeriesVolumeVisualiserForm, self).__init__() - self.time_series = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.time_series, name='time_series', - conditions=self.get_filters()) + self.time_series = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.time_series, name='time_series') + # conditions=self.get_filters()) + self.background = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.background, name='background') - mri_runtime_filter = FilterChain(fields=[FilterChain.datatype + '.gid'], operations=["=="], - values=['fk_volume_gid:fk_volume_gid']) - self.background = TraitDataTypeSelectField(TimeSeriesVolumeVisualiserModel.background, name='background', - runtime_conditions=('time_series', mri_runtime_filter)) @staticmethod def get_view_model():