diff --git a/requirements-dev.txt b/requirements-dev.txt index 5924a553..348c4bde 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,8 +11,9 @@ dash_auth==1.4.1 dash_core_components==1.15.0 dash_html_components==1.1.2 dash_renderer==1.9.0 +Werkzeug<=2.0.3 solvebio==2.12.0 pyyaml==5.3.1 click==7.1.2 ruamel.yaml==0.16.12 -pytest \ No newline at end of file +pytest diff --git a/solvebio/global_search.py b/solvebio/global_search.py index 22d77711..ed79b51e 100644 --- a/solvebio/global_search.py +++ b/solvebio/global_search.py @@ -23,6 +23,8 @@ def __init__( query=None, filters=None, entities=None, + entities_match='any', + vault_scope='all', ordering=None, limit=float('inf'), page_size=QueryBase.DEFAULT_PAGE_SIZE, @@ -37,6 +39,8 @@ def __init__( - `query` (optional): An optional query string (advanced search). - `filters` (optional): Filter or List of filter objects. - `entities` (optional): List of entity tuples to filter on (entity type, entity). + - `entities_match` (optional): Can be 'all' or 'any' (match any provided entity). + - `vault_scope` (optional): Can be 'all' or 'access'. - `ordering` (optional): List of fields to order the results by. - `limit` (optional): Maximum number of query results to return. - `page_size` (optional): Number of results to fetch per query page. @@ -49,12 +53,13 @@ def __init__( self._data_url = '/v2/search' self._query = query self._entities = entities + self._entities_match = entities_match + self._vault_scope = vault_scope self._ordering = ordering self._result_class = result_class self._debug = debug self._raw_results = raw_results self._error = None - self._is_join = False if filters: if isinstance(filters, Filter): @@ -97,6 +102,8 @@ def _clone(self, filters=None, entities=None, limit=None): ordering=self._ordering, page_size=self._page_size, result_class=self._result_class, + vault_scope=self._vault_scope, + entities_match=self._entities_match, debug=self._debug, client=self._client) new._filters += self._filters @@ -124,9 +131,6 @@ def __len__(self): SELECT * FROM [WHERE condition] [LIMIT number] ) """ - if self._is_join: - return len(self._buffer) - return super(GlobalSearch, self).__len__() def _build_query(self, **kwargs): @@ -148,6 +152,12 @@ def _build_query(self, **kwargs): if self._ordering is not None: q['ordering'] = self._ordering + if self._vault_scope is not None: + q['vault_scope'] = self._vault_scope + + if self._entities_match is not None: + q['entities_match'] = self._entities_match + if self._debug: q['debug'] = 'True' @@ -158,14 +168,20 @@ def _build_query(self, **kwargs): return q def execute(self, offset=0, **query): + def _process_result(result): + # Internally the client uses object_type, not type + result['object_type'] = result['type'] + if result['object_type'] == 'vault': + return Vault.construct_from(result) + else: + return Object.construct_from(result) + # Call superclass method execute super(GlobalSearch, self).execute(offset, **query) # Cast logical objects from response to Object/Vault instances if not self._raw_results: - self._response['results'] = [Vault.construct_from(result) if result['type'] == 'vault' - else Object.construct_from(result) - for result in self._response['results']] + self._response['results'] = [_process_result(i) for i in self._response['results']] def entity(self, **kwargs): """ @@ -198,3 +214,12 @@ def subjects_count(self): gs.execute() return gs._response.get('subjects_count') + + def vaults(self): + """Returns the list of vaults""" + + # Executes a query to get a full API response which contains vaults list + gs = self.limit(0) + gs.execute(include_vaults=True) + + return gs._response.get('vaults') diff --git a/solvebio/resource/object.py b/solvebio/resource/object.py index e48f30d2..7af07410 100644 --- a/solvebio/resource/object.py +++ b/solvebio/resource/object.py @@ -440,12 +440,9 @@ def __getattr__(self, name): try: return self[name] except KeyError as err: - # If the Object has a dataset_id, it is of object_type "dataset" - # If there is no dataset_id, either this Object is a file or folder - # or the resource has not yet been retrieved from the API. - if name in valid_dataset_attrs and self.dataset_id: + if name in valid_dataset_attrs and self['object_type'] == "dataset": return getattr( - Dataset(self.dataset_id, client=self._client), name) + Dataset(self['id'], client=self._client), name) raise AttributeError(*err.args) @@ -458,7 +455,7 @@ def dataset(self): "Only dataset objects have a Dataset resource. This is a {}" .format(self.object_type)) - return Dataset.retrieve(self.dataset_id, client=self._client) + return Dataset.retrieve(self['id'], client=self._client) @property def parent(self): @@ -560,7 +557,7 @@ def query(self, **params): from solvebio.query import QueryFile if self.is_dataset: - return Dataset(self.dataset_id, client=self._client).query(**params) + return Dataset(self['id'], client=self._client).query(**params) elif self.is_file: return QueryFile(self['id'], client=self._client, **params) else: diff --git a/solvebio/test/test_dataset_migrations.py b/solvebio/test/test_dataset_migrations.py index 0c39188c..397bf56a 100644 --- a/solvebio/test/test_dataset_migrations.py +++ b/solvebio/test/test_dataset_migrations.py @@ -117,10 +117,10 @@ def test_migration_target_dataset_object(self, Create): """Target is an Object of object_type=dataset""" Create.side_effect = fake_migration_create - source = self.client.Object(1, object_type='dataset') - source.dataset_id = source.id - target = self.client.Object(2, object_type='dataset') - target.dataset_id = target.id + source = self.client.Object(1) + source['object_type'] = 'dataset' + target = self.client.Object(2) + target['object_type'] = 'dataset' migration = source.migrate(target=target, follow=False) self.assertEqual(migration.source_id, source.id) self.assertEqual(migration.target_id, target.id)