mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Make sure orm filters operate on correct model
In practice we use ParsedFiler.filter_type to discriminate the different filter types. Filters that require the correct model class for a specific query are now functions that take a model_class and generate the correct filter.
This commit is contained in:
+16
-13
@@ -36,9 +36,12 @@ from six import string_types
|
||||
from galaxy import exceptions
|
||||
from galaxy import model
|
||||
from galaxy.model import tool_shed_install
|
||||
from galaxy.util import namedtuple
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
parsed_filter = namedtuple("ParsedFilter", "filter_type filter")
|
||||
|
||||
|
||||
# ==== accessors from base/controller.py
|
||||
def security_check(trans, item, check_ownership=False, check_accessible=False):
|
||||
@@ -323,18 +326,16 @@ class ModelManager(object):
|
||||
if not isinstance(filters, list):
|
||||
filters = [filters]
|
||||
for filter_ in filters:
|
||||
if self._is_fn_filter(filter_):
|
||||
fn_filters.append(filter_)
|
||||
else:
|
||||
if not hasattr(filter_, 'filter_type'):
|
||||
orm_filters.append(filter_)
|
||||
elif filter_.filter_type == 'function':
|
||||
fn_filters.append(filter_.filter)
|
||||
elif filter_.filter_type == 'orm_function':
|
||||
orm_filters.append(filter_.filter(self.model_class))
|
||||
else:
|
||||
orm_filters.append(filter_.filter)
|
||||
return (orm_filters, fn_filters)
|
||||
|
||||
def _is_fn_filter(self, filter_):
|
||||
"""
|
||||
Returns True if `filter_` is a functional filter to be applied after the SQL query.
|
||||
"""
|
||||
return callable(filter_)
|
||||
|
||||
def _orm_list(self, query=None, **kwargs):
|
||||
"""
|
||||
Sends kwargs to build the query return all models found.
|
||||
@@ -380,7 +381,7 @@ class ModelManager(object):
|
||||
"""
|
||||
if not ids:
|
||||
return []
|
||||
ids_filter = self.model_class.id.in_(ids)
|
||||
ids_filter = parsed_filter("orm", self.model_class.id.in_(ids))
|
||||
found = self.list(filters=self._munge_filters(ids_filter, filters), **kwargs)
|
||||
# TODO: this does not order by the original 'ids' array
|
||||
|
||||
@@ -899,6 +900,8 @@ class ModelFilterParser(HasAModelManager):
|
||||
|
||||
#: model class
|
||||
model_class = None
|
||||
subcontainer_model_class = None
|
||||
parsed_filter = parsed_filter
|
||||
|
||||
def __init__(self, app, **kwargs):
|
||||
"""
|
||||
@@ -998,7 +1001,7 @@ class ModelFilterParser(HasAModelManager):
|
||||
val = val_parser(val)
|
||||
|
||||
# curry/partial and fold the val in there now
|
||||
return lambda i: filter_fn(i, val)
|
||||
return self.parsed_filter(filter_type="function", filter=lambda i: filter_fn(i, val))
|
||||
|
||||
# ---- ORM filters
|
||||
def _parse_orm_filter(self, attr, op, val):
|
||||
@@ -1013,7 +1016,7 @@ class ModelFilterParser(HasAModelManager):
|
||||
# no column mapping (not whitelisted)
|
||||
return None
|
||||
if callable(column_map):
|
||||
return column_map(attr, op, val)
|
||||
return self.parsed_filter(filter_type="orm_function", filter=column_map(attr, op, val))
|
||||
# attr must be a whitelisted column by attr name or by key passed in column_map
|
||||
# note: column_map[ 'column' ] takes precedence
|
||||
if 'column' in column_map:
|
||||
@@ -1040,7 +1043,7 @@ class ModelFilterParser(HasAModelManager):
|
||||
val = val_parser(val)
|
||||
|
||||
orm_filter = op(val)
|
||||
return orm_filter
|
||||
return self.parsed_filter(filter_type="orm", filter=orm_filter)
|
||||
|
||||
#: these are the easier/shorter string equivalents to the python operator fn names that need '__' around them
|
||||
UNDERSCORED_OPS = ('lt', 'le', 'eq', 'ne', 'ge', 'gt')
|
||||
|
||||
@@ -153,8 +153,8 @@ class HistoryContentsManager(containers.ContainerManagerMixin):
|
||||
Note: does not include deleted/hidden contents.
|
||||
"""
|
||||
filters = [
|
||||
sql.column('deleted') == false(),
|
||||
sql.column('visible') == true()
|
||||
base.ModelFilterParser.parsed_filter("orm", sql.column('deleted') == false()),
|
||||
base.ModelFilterParser.parsed_filter("orm", sql.column('visible') == true())
|
||||
]
|
||||
contents_subquery = self._union_of_contents_query(history, filters=filters).subquery()
|
||||
statement = (sql.select([sql.column('state'), func.count('*')])
|
||||
@@ -250,7 +250,6 @@ class HistoryContentsManager(containers.ContainerManagerMixin):
|
||||
# cycle back over the union query to create an ordered list of the objects returned in queries 2 & 3 above
|
||||
contents = []
|
||||
filters = kwargs.get('filters') or []
|
||||
filters = [f for f in filters if callable(f)]
|
||||
# TODO: or as generator?
|
||||
for result in contents_results:
|
||||
result_type = self._get_union_type(result)
|
||||
@@ -263,8 +262,9 @@ class HistoryContentsManager(containers.ContainerManagerMixin):
|
||||
@staticmethod
|
||||
def passes_filters(content, filters):
|
||||
for filter_fn in filters:
|
||||
if not filter_fn(content):
|
||||
return False
|
||||
if filter_fn.filter_type == 'function':
|
||||
if not filter_fn.filter(content):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _union_of_contents_query(self, container, filters=None, limit=None, offset=None, order_by=None, **kwargs):
|
||||
@@ -287,12 +287,19 @@ class HistoryContentsManager(containers.ContainerManagerMixin):
|
||||
# query 1: create a union of common columns for which the component_classes can be filtered/limited
|
||||
contained_query = self._contents_common_query_for_contained(container.id if container else None)
|
||||
subcontainer_query = self._contents_common_query_for_subcontainer(container.id if container else None)
|
||||
|
||||
filters = filters or []
|
||||
# Apply filters that are specific to a model
|
||||
for orm_filter in filters:
|
||||
if orm_filter.filter_type == "orm_function":
|
||||
contained_query = contained_query.filter(orm_filter.filter(self.contained_class))
|
||||
subcontainer_query = subcontainer_query.filter(orm_filter.filter(self.subcontainer_class))
|
||||
contents_query = contained_query.union(subcontainer_query)
|
||||
|
||||
for orm_filter in (filters or []):
|
||||
if not callable(orm_filter):
|
||||
# skip python filters that need to be applied after the qeury
|
||||
contents_query = contents_query.filter(orm_filter)
|
||||
for orm_filter in filters:
|
||||
if orm_filter.filter_type == "orm":
|
||||
contents_query = contents_query.filter(orm_filter.filter)
|
||||
|
||||
contents_query = contents_query.order_by(*order_by)
|
||||
|
||||
if limit is not None:
|
||||
@@ -440,8 +447,8 @@ class HistoryContentsFilters(base.ModelFilterParser,
|
||||
tools.ToolFilterMixin):
|
||||
# surprisingly (but ominously), this works for both content classes in the union that's filtered
|
||||
model_class = model.HistoryDatasetAssociation
|
||||
subcontainer_model_class = model.HistoryDatasetCollectionAssociation
|
||||
|
||||
# TODO: history_content_type filter doesn't work with psycopg2: column does not exist (even with hybrid props)
|
||||
def _parse_orm_filter(self, attr, op, val):
|
||||
|
||||
# we need to use some manual/text/column fu here since some where clauses on the union don't work
|
||||
@@ -449,41 +456,43 @@ class HistoryContentsFilters(base.ModelFilterParser,
|
||||
# (and some of these are *not* a normal columns (especially 'state') anyway)
|
||||
# TODO: genericize these - can probably extract a _get_column( attr, ... ) or something
|
||||
# special cases...special cases everywhere
|
||||
if attr == 'history_content_type' and op == 'eq':
|
||||
if val == 'dataset':
|
||||
return sql.column('history_content_type') == 'dataset'
|
||||
if val == 'dataset_collection':
|
||||
return sql.column('history_content_type') == 'dataset_collection'
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
def get_filter(attr, op, val):
|
||||
if attr == 'history_content_type' and op == 'eq':
|
||||
if val in ('dataset', 'dataset_collection'):
|
||||
return sql.column('history_content_type') == val
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
|
||||
if attr == 'type_id':
|
||||
if op == 'eq':
|
||||
return sql.column('type_id') == val
|
||||
if op == 'in':
|
||||
return sql.column('type_id').in_(self.parse_type_id_list(val))
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
if attr == 'type_id':
|
||||
if op == 'eq':
|
||||
return sql.column('type_id') == val
|
||||
if op == 'in':
|
||||
return sql.column('type_id').in_(self.parse_type_id_list(val))
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
|
||||
if attr in ('update_time', 'create_time'):
|
||||
if op == 'ge':
|
||||
return sql.column(attr) >= self.parse_date(val)
|
||||
if op == 'le':
|
||||
return sql.column(attr) <= self.parse_date(val)
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
if attr in ('update_time', 'create_time'):
|
||||
if op == 'ge':
|
||||
return sql.column(attr) >= self.parse_date(val)
|
||||
if op == 'le':
|
||||
return sql.column(attr) <= self.parse_date(val)
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
|
||||
if attr == 'state':
|
||||
valid_states = model.Dataset.states.values()
|
||||
if op == 'eq':
|
||||
if val not in valid_states:
|
||||
self.raise_filter_err(attr, op, val, 'invalid state in filter')
|
||||
return sql.column('state') == val
|
||||
if op == 'in':
|
||||
states = [s for s in val.split(',') if s]
|
||||
for state in states:
|
||||
if state not in valid_states:
|
||||
self.raise_filter_err(attr, op, state, 'invalid state in filter')
|
||||
return sql.column('state').in_(states)
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
if attr == 'state':
|
||||
valid_states = model.Dataset.states.values()
|
||||
if op == 'eq':
|
||||
if val not in valid_states:
|
||||
self.raise_filter_err(attr, op, val, 'invalid state in filter')
|
||||
return sql.column('state') == val
|
||||
if op == 'in':
|
||||
states = [s for s in val.split(',') if s]
|
||||
for state in states:
|
||||
if state not in valid_states:
|
||||
self.raise_filter_err(attr, op, state, 'invalid state in filter')
|
||||
return sql.column('state').in_(states)
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
|
||||
column_filter = get_filter(attr, op, val)
|
||||
if column_filter is not None:
|
||||
return self.parsed_filter(filter_type='orm', filter=column_filter)
|
||||
return super(HistoryContentsFilters, self)._parse_orm_filter(attr, op, val)
|
||||
|
||||
def decode_type_id(self, type_id):
|
||||
|
||||
@@ -96,26 +96,36 @@ class TaggableDeserializerMixin(object):
|
||||
|
||||
class TaggableFilterMixin(object):
|
||||
|
||||
valid_ops = ('eq', 'contains', 'has')
|
||||
|
||||
def create_tag_filter(self, attr, op, val):
|
||||
target_model = getattr(model, "%sTagAssociation" % self.model_class.__name__)
|
||||
id_column = "%s_id" % target_model.table.name.rsplit('_tag_association')[0]
|
||||
if ':' not in val and op == 'eq':
|
||||
# We require an exact match and the tag to look for has no user_value,
|
||||
# so we can't just concatenate user_tname, ':' and user_vale
|
||||
column = target_model.table.c.user_tname
|
||||
else:
|
||||
|
||||
def _create_tag_filter(model_class=None):
|
||||
if op not in TaggableFilterMixin.valid_ops:
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
if model_class is None:
|
||||
return True
|
||||
class_name = model_class.__name__
|
||||
if class_name == 'HistoryDatasetCollectionAssociation':
|
||||
# Unfortunately we were a little inconsistent with out naming scheme
|
||||
class_name = 'HistoryDatasetCollection'
|
||||
target_model = getattr(model, "%sTagAssociation" % class_name)
|
||||
id_column = "%s_id" % target_model.table.name.rsplit('_tag_association')[0]
|
||||
column = target_model.table.c.user_tname + ":" + target_model.table.c.user_value
|
||||
if op == 'eq':
|
||||
cond = column == val
|
||||
elif op in ('contains', 'has'):
|
||||
cond = column.contains(val, autoescape=True)
|
||||
else:
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
return sql.expression.and_(
|
||||
# TODO: generalize to all sorts of tag associations
|
||||
self.model_class.table.c.id == getattr(target_model.table.c, id_column),
|
||||
cond
|
||||
)
|
||||
if op == 'eq':
|
||||
if ':' not in val:
|
||||
# We require an exact match and the tag to look for has no user_value,
|
||||
# so we can't just concatenate user_tname, ':' and user_vale
|
||||
cond = target_model.table.c.user_tname == val
|
||||
else:
|
||||
cond = column == val
|
||||
else:
|
||||
cond = column.contains(val, autoescape=True)
|
||||
return sql.expression.and_(
|
||||
model_class.table.c.id == getattr(target_model.table.c, id_column),
|
||||
cond
|
||||
)
|
||||
return _create_tag_filter
|
||||
|
||||
def _add_parsers(self):
|
||||
self.orm_filter_parsers.update({
|
||||
|
||||
@@ -96,17 +96,30 @@ class DynamicToolManager(ModelManager):
|
||||
class ToolFilterMixin(object):
|
||||
|
||||
def create_tool_filter(self, attr, op, val):
|
||||
if op == 'eq':
|
||||
cond = model.Job.table.c.tool_id == val
|
||||
elif op == 'contains':
|
||||
cond = model.Job.table.c.tool_id.contains(val, autoescape=True)
|
||||
else:
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
return sql.expression.and_(
|
||||
model.Job.table.c.id == model.JobToOutputDatasetAssociation.table.c.job_id,
|
||||
model.HistoryDatasetAssociation.table.c.id == model.JobToOutputDatasetAssociation.table.c.dataset_id,
|
||||
cond
|
||||
)
|
||||
|
||||
def _create_tool_filter(model_class=None):
|
||||
if op == 'eq':
|
||||
cond = model.Job.table.c.tool_id == val
|
||||
elif op == 'contains':
|
||||
cond = model.Job.table.c.tool_id.contains(val, autoescape=True)
|
||||
else:
|
||||
self.raise_filter_err(attr, op, val, 'bad op in filter')
|
||||
if model_class is model.HistoryDatasetAssociation:
|
||||
return sql.expression.and_(
|
||||
model.Job.table.c.id == model.JobToOutputDatasetAssociation.table.c.job_id,
|
||||
model.HistoryDatasetAssociation.table.c.id == model.JobToOutputDatasetAssociation.table.c.dataset_id,
|
||||
cond
|
||||
)
|
||||
elif model_class is model.HistoryDatasetCollectionAssociation:
|
||||
return sql.expression.and_(
|
||||
model.Job.id == model.JobToOutputDatasetAssociation.job_id,
|
||||
model.JobToOutputDatasetAssociation.dataset_id == model.DatasetCollectionElement.hda_id,
|
||||
model.DatasetCollectionElement.dataset_collection_id == model.HistoryDatasetCollectionAssociation.collection_id,
|
||||
cond,
|
||||
)
|
||||
else:
|
||||
return True
|
||||
return _create_tool_filter
|
||||
|
||||
def _add_parsers(self):
|
||||
self.orm_filter_parsers.update({
|
||||
|
||||
@@ -63,6 +63,8 @@ inflector = Inflector(English)
|
||||
log = get_logger(__name__)
|
||||
_lock = threading.RLock()
|
||||
|
||||
namedtuple = collections.namedtuple
|
||||
|
||||
CHUNK_SIZE = 65536 # 64k
|
||||
|
||||
DATABASE_MAX_STRING_SIZE = 32768
|
||||
|
||||
@@ -35,6 +35,7 @@ class DatasetsController(BaseAPIController, UsesVisualizationMixin):
|
||||
|
||||
def __init__(self, app):
|
||||
super(DatasetsController, self).__init__(app)
|
||||
self.history_manager = managers.histories.HistoryManager(app)
|
||||
self.hda_manager = managers.hdas.HDAManager(app)
|
||||
self.hda_serializer = managers.hdas.HDASerializer(app)
|
||||
self.hdca_serializer = managers.hdcas.HDCASerializer(app)
|
||||
@@ -55,6 +56,7 @@ class DatasetsController(BaseAPIController, UsesVisualizationMixin):
|
||||
trans,
|
||||
limit=500,
|
||||
offset=0,
|
||||
history_id=None,
|
||||
**kwd):
|
||||
"""
|
||||
GET /api/datasets/
|
||||
@@ -109,8 +111,11 @@ class DatasetsController(BaseAPIController, UsesVisualizationMixin):
|
||||
filter_params = self.parse_filter_params(kwd)
|
||||
filters = self.history_contents_filters.parse_filters(filter_params)
|
||||
order_by = self._parse_order_by(manager=self.history_contents_manager, order_by_string=kwd.get('order', 'create_time-dsc'))
|
||||
container = None
|
||||
if history_id:
|
||||
container = self.history_manager.get_accessible(self.decode_id(history_id), trans.user)
|
||||
contents = self.history_contents_manager.contents(
|
||||
container=None, filters=filters, limit=limit, offset=offset, order_by=order_by
|
||||
container=container, filters=filters, limit=limit, offset=offset, order_by=order_by
|
||||
)
|
||||
return [self.serializer_by_type[content.history_content_type].serialize_to_view(content, user=trans.user, trans=trans, view='summary') for content in contents]
|
||||
|
||||
|
||||
@@ -71,12 +71,12 @@ class DatasetsApiTestCase(api.ApiTestCase):
|
||||
assert len(self._get("datasets", payload).json()) == 1
|
||||
self.dataset_collection_populator.create_list_in_history(self.history_id,
|
||||
name="search by tool id",
|
||||
contents=["1\n2\n3"])
|
||||
payload = {'limit': 1, 'offset': 0, 'q': ['history_content_type', 'tool_id'],
|
||||
'qv': ['dataset_collection', 'upload1']}
|
||||
contents=["1\n2\n3"]).json()
|
||||
self.dataset_populator.wait_for_history(self.history_id)
|
||||
payload = {'limit': 10, 'offset': 0, 'history_id': self.history_id, 'q': ['name', 'tool_id'],
|
||||
'qv': ['search by tool id', 'upload1']}
|
||||
result = self._get("datasets", payload).json()
|
||||
assert len(result) == 1
|
||||
assert result[0]['name'] == 'search by tool id'
|
||||
assert result[0]['name'] == 'search by tool id', result
|
||||
payload = {'limit': 1, 'offset': 0, 'q': ['history_content_type', 'tool_id'],
|
||||
'qv': ['dataset_collection', 'uploadX']}
|
||||
result = self._get("datasets", payload).json()
|
||||
|
||||
@@ -100,14 +100,18 @@ class BaseTestCase(unittest.TestCase):
|
||||
self.assertTrue(True, 'is uuid: ' + item)
|
||||
|
||||
def assertORMFilter(self, item, msg=None):
|
||||
if not isinstance(item, (sqlalchemy.sql.elements.BinaryExpression, sqlalchemy.sql.elements.BooleanClauseList)):
|
||||
self.fail('Not an orm filter: ' + str(type(item)))
|
||||
self.assertTrue(True, msg or ('is an orm filter: ' + str(item)))
|
||||
if not isinstance(item.filter, (sqlalchemy.sql.elements.BinaryExpression, sqlalchemy.sql.elements.BooleanClauseList)):
|
||||
self.fail('Not an orm filter: ' + str(type(item.filter)))
|
||||
self.assertTrue(True, msg or ('is an orm filter: ' + str(item.filter)))
|
||||
|
||||
def assertORMFunctionFilter(self, item, msg=None):
|
||||
assert item.filter_type == 'orm_function'
|
||||
assert callable(item.filter)
|
||||
|
||||
def assertFnFilter(self, item, msg=None):
|
||||
if not item or not callable(item):
|
||||
self.fail('Not a fn filter: ' + str(type(item)))
|
||||
self.assertTrue(True, msg or ('is a fn filter: ' + str(item)))
|
||||
if not item.filter or not callable(item.filter):
|
||||
self.fail('Not a fn filter: ' + str(type(item.filter)))
|
||||
self.assertTrue(True, msg or ('is a fn filter: ' + str(item.filter)))
|
||||
|
||||
def assertIsJsonifyable(self, item):
|
||||
# TODO: use galaxy's override
|
||||
|
||||
@@ -655,8 +655,8 @@ class HDAFilterParserTestCase(HDATestCase):
|
||||
self.assertORMFilter(self.filter_parser.parse_filter('state', 'in', ['queued', 'running']))
|
||||
self.assertORMFilter(self.filter_parser.parse_filter('visible', 'eq', True))
|
||||
# taggable
|
||||
self.assertORMFilter(self.filter_parser.parse_filter('tag', 'eq', 'wot'))
|
||||
self.assertORMFilter(self.filter_parser.parse_filter('tag', 'has', 'wot'))
|
||||
self.assertORMFunctionFilter(self.filter_parser.parse_filter('tag', 'eq', 'wot'))
|
||||
self.assertORMFunctionFilter(self.filter_parser.parse_filter('tag', 'has', 'wot'))
|
||||
# genomebuild
|
||||
self.assertFnFilter(self.filter_parser.parse_filter('genome_build', 'eq', 'wot'))
|
||||
self.assertFnFilter(self.filter_parser.parse_filter('genome_build', 'contains', 'wot'))
|
||||
|
||||
@@ -8,7 +8,7 @@ import unittest
|
||||
from sqlalchemy import column, desc, false, true
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
from galaxy.managers import collections, hdas, history_contents
|
||||
from galaxy.managers import base, collections, hdas, history_contents
|
||||
from galaxy.managers.histories import HistoryManager
|
||||
from .base import BaseTestCase
|
||||
from .base import CreatesCollectionsMixin
|
||||
@@ -17,6 +17,7 @@ default_password = '123456'
|
||||
user2_data = dict(email='user2@user2.user2', username='user2', password=default_password)
|
||||
user3_data = dict(email='user3@user3.user3', username='user3', password=default_password)
|
||||
user4_data = dict(email='user4@user4.user4', username='user4', password=default_password)
|
||||
parsed_filter = base.ModelFilterParser.parsed_filter
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -135,15 +136,15 @@ class HistoryAsContainerTestCase(HistoryAsContainerBaseTestCase):
|
||||
self.app.model.context.flush()
|
||||
|
||||
# TODO: cross db compat?
|
||||
filters = [text('deleted = 1')]
|
||||
filters = [parsed_filter(filter_type="orm", filter=text('deleted = 1'))]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), deleted)
|
||||
|
||||
# even stranger that sqlalx can use the first model in the union (HDA) for columns across the union
|
||||
HDA = self.hda_manager.model_class
|
||||
self.assertEqual(self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true()]), deleted)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true())]), deleted)
|
||||
filter_limited_contents = self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true()], limit=2, offset=1)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true())], limit=2, offset=1)
|
||||
self.assertEqual(filter_limited_contents, deleted[1:])
|
||||
|
||||
self.log("should allow filter on visible")
|
||||
@@ -153,43 +154,43 @@ class HistoryAsContainerTestCase(HistoryAsContainerBaseTestCase):
|
||||
invisible = [contents[2], contents[5], contents[6]]
|
||||
self.app.model.context.flush()
|
||||
|
||||
filters = [text('visible = 0')]
|
||||
filters = [parsed_filter("orm", text('visible = 0'))]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), invisible)
|
||||
self.assertEqual(self.contents_manager.contents(history,
|
||||
filters=[HDA.visible == false()]), invisible)
|
||||
filters=[parsed_filter("orm", HDA.visible == false())]), invisible)
|
||||
filter_limited_contents = self.contents_manager.contents(history,
|
||||
filters=[HDA.visible == false()], limit=2, offset=1)
|
||||
filters=[parsed_filter("orm", HDA.visible == false())], limit=2, offset=1)
|
||||
self.assertEqual(filter_limited_contents, invisible[1:])
|
||||
|
||||
self.log("should allow filtering more than one attribute")
|
||||
deleted_and_invisible = [contents[6]]
|
||||
|
||||
filters = [text('deleted = 1'), text('visible = 0')]
|
||||
filters = [parsed_filter("orm", text('deleted = 1')), parsed_filter("orm", text('visible = 0'))]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), deleted_and_invisible)
|
||||
self.assertEqual(self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true(), HDA.visible == false()]), deleted_and_invisible)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true()), parsed_filter("orm", HDA.visible == false())]), deleted_and_invisible)
|
||||
offset_too_far = self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true(), HDA.visible == false()], limit=2, offset=1)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true()), parsed_filter("orm", HDA.visible == false())], limit=2, offset=1)
|
||||
self.assertEqual(offset_too_far, [])
|
||||
|
||||
self.log("should allow filtering more than one attribute")
|
||||
deleted_and_invisible = [contents[6]]
|
||||
# note the two syntaxes both work
|
||||
self.assertEqual(self.contents_manager.contents(history,
|
||||
filters=[text('deleted = 1'), text('visible = 0')]), deleted_and_invisible)
|
||||
filters=[parsed_filter("orm", text('deleted = 1')), parsed_filter("orm", text('visible = 0'))]), deleted_and_invisible)
|
||||
self.assertEqual(self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true(), HDA.visible == false()]), deleted_and_invisible)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true()), parsed_filter("orm", HDA.visible == false())]), deleted_and_invisible)
|
||||
offset_too_far = self.contents_manager.contents(history,
|
||||
filters=[HDA.deleted == true(), HDA.visible == false()], limit=2, offset=1)
|
||||
filters=[parsed_filter("orm", HDA.deleted == true()), parsed_filter("orm", HDA.visible == false())], limit=2, offset=1)
|
||||
self.assertEqual(offset_too_far, [])
|
||||
|
||||
self.log("should allow filtering using like")
|
||||
# find 'hda-4'
|
||||
self.assertEqual([contents[4]],
|
||||
self.contents_manager.contents(history, filters=[HDA.name.like('%-4')]))
|
||||
self.contents_manager.contents(history, filters=[parsed_filter("orm", HDA.name.like('%-4'))]))
|
||||
# the collections added above have the default name 'test collection'
|
||||
self.assertEqual(self.contents_manager.subcontainers(history),
|
||||
self.contents_manager.contents(history, filters=[HDA.name.like('%collect%')]))
|
||||
self.contents_manager.contents(history, filters=[parsed_filter("orm", HDA.name.like('%collect%'))]))
|
||||
|
||||
def test_order_by(self):
|
||||
user2 = self.user_manager.create(**user2_data)
|
||||
@@ -245,7 +246,7 @@ class HistoryAsContainerTestCase(HistoryAsContainerBaseTestCase):
|
||||
update_time = item.collection.update_time
|
||||
return update_time
|
||||
|
||||
results = self.contents_manager.contents(history, filters=[column('update_time') >= update_time])
|
||||
results = self.contents_manager.contents(history, filters=[parsed_filter("orm", column('update_time') >= update_time)])
|
||||
self.assertEqual(results, [contents[3]])
|
||||
|
||||
def test_filtered_counting(self):
|
||||
@@ -269,11 +270,11 @@ class HistoryAsContainerTestCase(HistoryAsContainerBaseTestCase):
|
||||
self.app.model.context.flush()
|
||||
|
||||
HDA = self.hda_manager.model_class
|
||||
self.assertEqual(self.contents_manager.contents_count(history, filters=[HDA.deleted == true()]), 3)
|
||||
filters = [text('visible = 0')]
|
||||
self.assertEqual(self.contents_manager.contents_count(history, filters=[parsed_filter("orm", HDA.deleted == true())]), 3)
|
||||
filters = [parsed_filter("orm", text('visible = 0'))]
|
||||
self.assertEqual(self.contents_manager.contents_count(history, filters=filters), 3)
|
||||
|
||||
filters = [text('deleted = 1'), text('visible = 0')]
|
||||
filters = [parsed_filter("orm", text('deleted = 1')), parsed_filter("orm", text('visible = 0'))]
|
||||
self.assertEqual(self.contents_manager.contents_count(history, filters=filters), 1)
|
||||
|
||||
def test_type_id(self):
|
||||
@@ -286,13 +287,13 @@ class HistoryAsContainerTestCase(HistoryAsContainerBaseTestCase):
|
||||
contents.append(self.add_list_collection_to_history(history, contents[4:6]))
|
||||
|
||||
self.log("should be able to use eq and in with hybrid type_id")
|
||||
filters = [column('type_id') == u'dataset-2']
|
||||
filters = [parsed_filter("orm", column('type_id') == u'dataset-2')]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), [contents[1]])
|
||||
filters = [column('type_id').in_([u'dataset-1', u'dataset-3'])]
|
||||
filters = [parsed_filter("orm", column('type_id').in_([u'dataset-1', u'dataset-3']))]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), [contents[0], contents[2]])
|
||||
filters = [column('type_id') == u'dataset_collection-1']
|
||||
filters = [parsed_filter("orm", column('type_id') == u'dataset_collection-1')]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), [contents[3]])
|
||||
filters = [column('type_id').in_([u'dataset-2', u'dataset_collection-2'])]
|
||||
filters = [parsed_filter("orm", column('type_id').in_([u'dataset-2', u'dataset_collection-2']))]
|
||||
self.assertEqual(self.contents_manager.contents(history, filters=filters), [contents[1], contents[6]])
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ default_password = '123456'
|
||||
user2_data = dict(email='user2@user2.user2', username='user2', password=default_password)
|
||||
user3_data = dict(email='user3@user3.user3', username='user3', password=default_password)
|
||||
user4_data = dict(email='user4@user4.user4', username='user4', password=default_password)
|
||||
parsed_filter = base.ModelFilterParser.parsed_filter
|
||||
|
||||
|
||||
class HistoryManagerTestCase(BaseTestCase):
|
||||
@@ -697,7 +698,7 @@ class HistoryFiltersTestCase(BaseTestCase):
|
||||
self.assertEqual(len(filters), 3)
|
||||
|
||||
self.log('values should be parsed')
|
||||
self.assertIsInstance(filters[1].right, sqlalchemy.sql.elements.True_)
|
||||
self.assertIsInstance(filters[1].filter.right, sqlalchemy.sql.elements.True_)
|
||||
|
||||
def test_parse_filters_invalid_filters(self):
|
||||
self.log('should error on non-column attr')
|
||||
@@ -763,7 +764,7 @@ class HistoryFiltersTestCase(BaseTestCase):
|
||||
history3 = self.history_manager.create(name='history3', user=user2)
|
||||
|
||||
filters = self.filter_parser.parse_filters([('annotation', 'has', 'no play'), ])
|
||||
anno_filter = filters[0]
|
||||
anno_filter = filters[0].filter
|
||||
|
||||
history3.add_item_annotation(self.trans.sa_session, user2, history3, "All work and no play")
|
||||
self.trans.sa_session.flush()
|
||||
@@ -797,7 +798,7 @@ class HistoryFiltersTestCase(BaseTestCase):
|
||||
self.log('should have parsed out a single filter')
|
||||
self.assertEqual(len(filters), 1)
|
||||
|
||||
filter_ = filters[0]
|
||||
filter_ = filters[0].filter
|
||||
fake = galaxy_mock.OpenObject()
|
||||
fake.name = '123'
|
||||
self.log('123 should return true through the filter')
|
||||
|
||||
Reference in New Issue
Block a user