diff --git a/framework_tvb/tvb/adapters/datatypes/db/fcd.py b/framework_tvb/tvb/adapters/datatypes/db/fcd.py index 8a30038163..7f53a93b8e 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/fcd.py +++ b/framework_tvb/tvb/adapters/datatypes/db/fcd.py @@ -43,6 +43,12 @@ class FcdIndex(DataTypeMatrix): labels_ordering = Column(String) + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + def fill_from_has_traits(self, datatype): # type: (Fcd) -> None super(FcdIndex, self).fill_from_has_traits(datatype) diff --git a/framework_tvb/tvb/adapters/datatypes/db/graph.py b/framework_tvb/tvb/adapters/datatypes/db/graph.py index bb00594250..462c381726 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/graph.py +++ b/framework_tvb/tvb/adapters/datatypes/db/graph.py @@ -65,6 +65,12 @@ class CorrelationCoefficientsIndex(DataTypeMatrix): labels_ordering = Column(String) + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + def fill_from_has_traits(self, datatype): # type: (CorrelationCoefficients) -> None super(CorrelationCoefficientsIndex, self).fill_from_has_traits(datatype) diff --git a/framework_tvb/tvb/adapters/datatypes/db/mode_decompositions.py b/framework_tvb/tvb/adapters/datatypes/db/mode_decompositions.py index f974a45696..1c100b0c1a 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/mode_decompositions.py +++ b/framework_tvb/tvb/adapters/datatypes/db/mode_decompositions.py @@ -49,6 +49,12 @@ def fill_from_has_traits(self, datatype): super(PrincipalComponentsIndex, self).fill_from_has_traits(datatype) self.fk_source_gid = datatype.source.gid + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + class IndependentComponentsIndex(DataType): id = Column(Integer, ForeignKey(DataType.id), primary_key=True) @@ -70,6 +76,12 @@ def fill_from_has_traits(self, datatype): self.ndim = len(datatype.unmixing_matrix.shape) self.array_has_complex = numpy.iscomplex(datatype.unmixing_matrix).any().item() + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + @property def parsed_shape(self): try: diff --git a/framework_tvb/tvb/adapters/datatypes/db/spectral.py b/framework_tvb/tvb/adapters/datatypes/db/spectral.py index 581e3fb4f9..1014edec44 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/spectral.py +++ b/framework_tvb/tvb/adapters/datatypes/db/spectral.py @@ -55,6 +55,12 @@ def fill_from_has_traits(self, datatype): self.max_frequency = datatype.max_frequency self.fk_source_gid = datatype.source.gid.hex + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + class WaveletCoefficientsIndex(DataTypeMatrix): id = Column(Integer, ForeignKey(DataTypeMatrix.id), primary_key=True) @@ -83,6 +89,12 @@ def fill_from_has_traits(self, datatype): self.frequencies_min, self.frequencies_max, _ = from_ndarray(datatype.frequencies) self.fk_source_gid = datatype.source.gid.hex + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + class CoherenceSpectrumIndex(DataTypeMatrix): id = Column(Integer, ForeignKey(DataTypeMatrix.id), primary_key=True) @@ -101,6 +113,12 @@ def fill_from_has_traits(self, datatype): self.frequencies_min, self.frequencies_max, _ = from_ndarray(datatype.frequency) self.fk_source_gid = datatype.source.gid.hex + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + class ComplexCoherenceSpectrumIndex(DataTypeMatrix): id = Column(Integer, ForeignKey(DataTypeMatrix.id), primary_key=True) @@ -124,3 +142,9 @@ def fill_from_has_traits(self, datatype): self.frequency_step = datatype.freq_step self.max_frequency = datatype.max_freq self.fk_source_gid = datatype.source.gid.hex + + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict diff --git a/framework_tvb/tvb/adapters/datatypes/db/temporal_correlations.py b/framework_tvb/tvb/adapters/datatypes/db/temporal_correlations.py index 9197e747b3..1fdb151960 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/temporal_correlations.py +++ b/framework_tvb/tvb/adapters/datatypes/db/temporal_correlations.py @@ -44,6 +44,12 @@ class CrossCorrelationIndex(DataTypeMatrix): labels_ordering = Column(String, nullable=False) + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.source.labels_ordering + labels_dict["labels_dimensions"] = self.source.labels_dimensions + return labels_dict + def fill_from_has_traits(self, datatype): # type: (CrossCorrelation) -> None super(CrossCorrelationIndex, self).fill_from_has_traits(datatype) diff --git a/framework_tvb/tvb/adapters/datatypes/db/time_series.py b/framework_tvb/tvb/adapters/datatypes/db/time_series.py index 2967a14521..446185826d 100644 --- a/framework_tvb/tvb/adapters/datatypes/db/time_series.py +++ b/framework_tvb/tvb/adapters/datatypes/db/time_series.py @@ -59,6 +59,12 @@ class TimeSeriesIndex(DataType): has_volume_mapping = Column(Boolean, nullable=False, default=False) has_surface_mapping = Column(Boolean, nullable=False, default=False) + def get_extra_info(self): + labels_dict = {} + labels_dict["labels_ordering"] = self.labels_ordering + labels_dict["labels_dimensions"] = self.labels_dimensions + return labels_dict + def fill_from_has_traits(self, datatype): # type: (TimeSeries) -> None super(TimeSeriesIndex, self).fill_from_has_traits(datatype) diff --git a/framework_tvb/tvb/core/entities/model/model_datatype.py b/framework_tvb/tvb/core/entities/model/model_datatype.py index 6c843e6613..227932f718 100644 --- a/framework_tvb/tvb/core/entities/model/model_datatype.py +++ b/framework_tvb/tvb/core/entities/model/model_datatype.py @@ -120,6 +120,9 @@ class DataType(HasTraitsIndex): # Transient info fixed_generic_attributes = False + def get_extra_info(self): + return {} + def __init__(self, gid=None, **kwargs): self.gid = gid diff --git a/framework_tvb/tvb/core/entities/storage/datatype_dao.py b/framework_tvb/tvb/core/entities/storage/datatype_dao.py index c2d66f4506..236a2afa64 100644 --- a/framework_tvb/tvb/core/entities/storage/datatype_dao.py +++ b/framework_tvb/tvb/core/entities/storage/datatype_dao.py @@ -375,7 +375,11 @@ def get_datatype_details(self, datatype_gid): return result_dt - def get_datatype_by_gid(self, gid, load_lazy=True): + def get_datatype_extra_info(self, datatype_gid): + datatype = self.get_datatype_by_gid(datatype_gid, False, True) + return datatype.get_extra_info() + + def get_datatype_by_gid(self, gid, load_lazy=True, load_lazy_extra_info=False): """ Retrieve a DataType DB reference by a global identifier. """ @@ -393,6 +397,8 @@ def get_datatype_by_gid(self, gid, load_lazy=True): result_dt.parent_operation.algorithm.algorithm_category result_dt.parent_operation.operation_group result_dt._parent_burst + if load_lazy_extra_info: + result_dt.get_extra_info() return result_dt except NoResultFound as excep: diff --git a/framework_tvb/tvb/interfaces/rest/client/datatype/datatype_api.py b/framework_tvb/tvb/interfaces/rest/client/datatype/datatype_api.py index d7495ad831..1f55fa82f2 100644 --- a/framework_tvb/tvb/interfaces/rest/client/datatype/datatype_api.py +++ b/framework_tvb/tvb/interfaces/rest/client/datatype/datatype_api.py @@ -67,6 +67,14 @@ def get_operations_for_datatype(self, datatype_gid): }))) return response, AlgorithmDto + @handle_response + def get_extra_info(self, datatype_gid): + response = self.secured_request().get( + self.build_request_url(RestLink.DATATYPE_EXTRA_INFO.compute_url(True, { + LinkPlaceholder.DATATYPE_GID.value: datatype_gid + }))) + return response, dict + def load_datatype_from_file(self, datatype_path): datatype, _ = h5.load_with_links(datatype_path) return datatype diff --git a/framework_tvb/tvb/interfaces/rest/client/examples/fire_simulation.py b/framework_tvb/tvb/interfaces/rest/client/examples/fire_simulation.py index c79a8cabbb..f45872a4a7 100644 --- a/framework_tvb/tvb/interfaces/rest/client/examples/fire_simulation.py +++ b/framework_tvb/tvb/interfaces/rest/client/examples/fire_simulation.py @@ -103,6 +103,17 @@ def fire_simulation_example(tvb_client_instance): operation_gid = tvb_client_instance.launch_operation(project_gid, FourierAdapter, fourier_model) logger.info("Fourier Analyzer operation has launched with gid {}".format(operation_gid)) + data_in_project = tvb_client_instance.get_data_in_project(project_gid) + logger.info("We have {} datatypes".format(len(data_in_project))) + ggid = None + for datatype in data_in_project: + if datatype.type == 'FourierSpectrum': + ggid = datatype.gid + break + + extra_info = tvb_client_instance.get_extra_info(ggid) + logger.info("The extra information for Fourier {}".format(extra_info)) + logger.info("Download the connectivity file...") connectivity_path = tvb_client_instance.retrieve_datatype(connectivity_gid, tvb_client_instance.temp_folder) logger.info("The connectivity file location is: {}".format(connectivity_path)) diff --git a/framework_tvb/tvb/interfaces/rest/client/tvb_client.py b/framework_tvb/tvb/interfaces/rest/client/tvb_client.py index 6e396d9301..e7b06f9365 100644 --- a/framework_tvb/tvb/interfaces/rest/client/tvb_client.py +++ b/framework_tvb/tvb/interfaces/rest/client/tvb_client.py @@ -237,3 +237,9 @@ def get_operation_results(self, operation_gid): operation if it has finished and an empty list, if the operation is still running, has failed or simply has no results. """ return self.operation_api.get_operations_results(operation_gid) + + def get_extra_info(self, datatype_gid): + """ + Given an datatype gid, this function returns a dict containing the extra information of the datatype. + """ + return self.datatype_api.get_extra_info(datatype_gid) diff --git a/framework_tvb/tvb/interfaces/rest/commons/strings.py b/framework_tvb/tvb/interfaces/rest/commons/strings.py index 569e7d8d40..beaaf5fbd4 100644 --- a/framework_tvb/tvb/interfaces/rest/commons/strings.py +++ b/framework_tvb/tvb/interfaces/rest/commons/strings.py @@ -86,6 +86,7 @@ class RestLink(Enum): # DATATYPES GET_DATATYPE = "/{" + LinkPlaceholder.DATATYPE_GID.value + "}" DATATYPE_OPERATIONS = "/{" + LinkPlaceholder.DATATYPE_GID.value + "}/operations" + DATATYPE_EXTRA_INFO = "/{" + LinkPlaceholder.DATATYPE_GID.value + "}/extra_info" # OPERATIONS LAUNCH_OPERATION = "/{" + LinkPlaceholder.PROJECT_GID.value + "}/algorithm/{" + LinkPlaceholder.ALG_MODULE.value + "}/{" + LinkPlaceholder.ALG_CLASSNAME.value + "}" @@ -111,7 +112,7 @@ def compute_url(self, include_namespace=False, values=None): _namespace_url_dict = { RestNamespace.USERS: [RestLink.LOGIN, RestLink.PROJECTS, RestLink.USEFUL_URLS], RestNamespace.PROJECTS: [RestLink.DATA_IN_PROJECT, RestLink.OPERATIONS_IN_PROJECT, RestLink.PROJECT_MEMBERS], - RestNamespace.DATATYPES: [RestLink.GET_DATATYPE, RestLink.DATATYPE_OPERATIONS], + RestNamespace.DATATYPES: [RestLink.GET_DATATYPE, RestLink.DATATYPE_OPERATIONS, RestLink.DATATYPE_EXTRA_INFO], RestNamespace.OPERATIONS: [RestLink.LAUNCH_OPERATION, RestLink.OPERATION_STATUS, RestLink.OPERATION_RESULTS], RestNamespace.SIMULATION: [RestLink.FIRE_SIMULATION] } diff --git a/framework_tvb/tvb/interfaces/rest/server/facades/datatype_facade.py b/framework_tvb/tvb/interfaces/rest/server/facades/datatype_facade.py index c35d65c0bb..a771306d46 100644 --- a/framework_tvb/tvb/interfaces/rest/server/facades/datatype_facade.py +++ b/framework_tvb/tvb/interfaces/rest/server/facades/datatype_facade.py @@ -32,7 +32,7 @@ from tvb.core.entities.storage import dao from tvb.core.neocom.h5 import h5_file_for_index from tvb.core.services.algorithm_service import AlgorithmService -from tvb.interfaces.rest.commons.dtos import AlgorithmDto +from tvb.interfaces.rest.commons.dtos import AlgorithmDto, DataTypeDto class DatatypeFacade: @@ -49,3 +49,11 @@ def get_datatype_operations(self, datatype_gid): datatype = dao.get_datatype_by_gid(datatype_gid) _, filtered_adapters, _ = self.algorithm_service.get_launchable_algorithms_for_datatype(datatype, categories) return [AlgorithmDto(algorithm) for algorithm in filtered_adapters] + + @staticmethod + def get_extra_info(datatype_gid): + extra_info = dao.get_datatype_extra_info(datatype_gid) + if extra_info is None: + return None + + return extra_info diff --git a/framework_tvb/tvb/interfaces/rest/server/resources/datatype/datatype_resource.py b/framework_tvb/tvb/interfaces/rest/server/resources/datatype/datatype_resource.py index 2791deaea2..e3e82a9159 100644 --- a/framework_tvb/tvb/interfaces/rest/server/resources/datatype/datatype_resource.py +++ b/framework_tvb/tvb/interfaces/rest/server/resources/datatype/datatype_resource.py @@ -47,6 +47,18 @@ def get(self, datatype_gid): file_name = os.path.basename(h5_file_path) return flask.send_file(h5_file_path, as_attachment=True, attachment_filename=file_name) +class GetExtraInfoForDatatypeResource(RestResource): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.datatypes_facade = DatatypeFacade() + + @check_permission(DataTypeAccessPermission, 'datatype_gid') + def get(self, datatype_gid): + """ + :return the results of DataType. + """ + return self.datatypes_facade.get_extra_info(datatype_gid) class GetOperationsForDatatypeResource(RestResource): diff --git a/framework_tvb/tvb/interfaces/rest/server/run.py b/framework_tvb/tvb/interfaces/rest/server/run.py index 5aae1bd8bd..a7941c2cb7 100644 --- a/framework_tvb/tvb/interfaces/rest/server/run.py +++ b/framework_tvb/tvb/interfaces/rest/server/run.py @@ -41,7 +41,7 @@ from tvb.interfaces.rest.commons.strings import RestNamespace, RestLink, LinkPlaceholder, Strings from tvb.interfaces.rest.server.decorators.encoders import CustomFlaskEncoder from tvb.interfaces.rest.server.resources.datatype.datatype_resource import RetrieveDatatypeResource, \ - GetOperationsForDatatypeResource + GetOperationsForDatatypeResource, GetExtraInfoForDatatypeResource from tvb.interfaces.rest.server.resources.operation.operation_resource import GetOperationStatusResource, \ GetOperationResultsResource, LaunchOperationResource from tvb.interfaces.rest.server.resources.project.project_resource import GetOperationsInProjectResource, \ @@ -110,6 +110,8 @@ def initialize_flask(): values={LinkPlaceholder.DATATYPE_GID.value: ''})) name_space_datatypes.add_resource(GetOperationsForDatatypeResource, RestLink.DATATYPE_OPERATIONS.compute_url( values={LinkPlaceholder.DATATYPE_GID.value: ''})) + name_space_datatypes.add_resource(GetExtraInfoForDatatypeResource, RestLink.DATATYPE_EXTRA_INFO.compute_url( + values={LinkPlaceholder.DATATYPE_GID.value: ''})) # Operations namespace name_space_operations = api.namespace(build_path(RestNamespace.OPERATIONS),