Merge pull request #15786 from ahmedhamidawan/related_filter_refactor

Add API test and refactor code for related:hid history filter
This commit is contained in:
John Chilton
2023-03-14 15:42:17 -04:00
committed by GitHub
3 changed files with 68 additions and 25 deletions
+30 -19
View File
@@ -531,34 +531,23 @@ class HistoryContentsFilters(
def parse_query_filters_with_relations(self, query_filters: ValueFilterQueryParams, history_id):
"""Parse query filters but consider case where related filter is included."""
if query_filters.q and query_filters.qv and "related-eq" in query_filters.q:
qv_index = query_filters.q.index("related-eq")
has_related_q = [q for q in ("related-eq", "related") if query_filters.q and q in query_filters.q]
if query_filters.q and query_filters.qv and has_related_q:
qv_index = query_filters.q.index(has_related_q[0])
qv_hid = query_filters.qv[qv_index]
# Type check whether hid is int
if not qv_hid.isdigit():
raise glx_exceptions.RequestParameterInvalidException(
"unparsable value for filter",
"unparsable value for related filter",
column="related",
operation="eq",
value=qv_hid,
ValueError="invalid type in filter",
)
# Make new q and qv excluding related filter
new_q = [x for i, x in enumerate(query_filters.q) if i != qv_index]
new_qv = [x for i, x in enumerate(query_filters.qv) if i != qv_index]
# Get list of related item hids from job_connections manager
job_connections_manager = JobConnectionsManager(self.app.model.session)
related = job_connections_manager.get_related_hids(history_id, qv_hid)
# Make new query_filters with updated list of related hids for given hid
new_q.append("related-eq")
new_qv.append(json.dumps(related))
query_filters_with_relations = ValueFilterQueryParams(
q=new_q,
qv=new_qv,
query_filters_with_relations = self.get_query_filters_with_relations(
query_filters=query_filters, related_q=has_related_q[0], history_id=history_id
)
return super().parse_query_filters(query_filters_with_relations)
return super().parse_query_filters(query_filters)
@@ -617,6 +606,30 @@ class HistoryContentsFilters(
return self.parsed_filter(filter_type="orm", filter=column_filter)
return super()._parse_orm_filter(attr, op, val)
def get_query_filters_with_relations(self, query_filters: ValueFilterQueryParams, related_q: str, history_id):
"""Return `query_filters_with_relations` changing `related:hid` to `related:[hid1, hid2, ...]`."""
if query_filters.q and query_filters.qv:
qv_index = query_filters.q.index(related_q)
qv_hid = query_filters.qv[qv_index]
# Make new q and qv excluding related filter
new_q = [x for i, x in enumerate(query_filters.q) if i != qv_index]
new_qv = [x for i, x in enumerate(query_filters.qv) if i != qv_index]
# Get list of related item hids from job_connections manager
job_connections_manager = JobConnectionsManager(self.app.model.session)
related = job_connections_manager.get_related_hids(history_id, int(qv_hid))
# Make new query_filters with updated list of related hids for given hid
new_q.append("related-eq")
new_qv.append(json.dumps(related))
query_filters_with_relations = ValueFilterQueryParams(
q=new_q,
qv=new_qv,
)
return query_filters_with_relations
return query_filters
def decode_type_id(self, type_id):
TYPE_ID_SEP = "-"
split = type_id.split(TYPE_ID_SEP, 1)
@@ -638,8 +651,6 @@ class HistoryContentsFilters(
self.orm_filter_parsers.update(
{
"history_content_type": {"op": ("eq")},
# maybe remove related from here, as there's no corresponding field?
"related": {"op": ("eq")},
"type_id": {"op": ("eq", "in"), "val": self.parse_type_id_list},
"hid": {"op": ("eq", "ge", "le", "gt", "lt"), "val": int},
# TODO: needs a different val parser - but no way to add to the above
+6 -6
View File
@@ -18,7 +18,7 @@ class JobConnectionsManager:
def __init__(self, sa_session: galaxy_scoped_session):
self.sa_session = sa_session
def get_connections_graph(self, id, src):
def get_connections_graph(self, id: int, src: str):
"""Get connections graph of inputs and outputs for given item id"""
if src == "HistoryDatasetAssociation":
output_selects = self.outputs_derived_from_input_hda(id)
@@ -34,7 +34,7 @@ class JobConnectionsManager:
result["inputs"] = self._get_union_results(*input_selects)
return result
def get_related_hids(self, history_id, hid):
def get_related_hids(self, history_id, hid: int):
"""Get connections graph of inputs and outputs for given item hid from the given history_id"""
# Get id(s) and src(s) for the given hid
items_by_hid = self.sa_session.execute(
@@ -63,7 +63,7 @@ class JobConnectionsManager:
result.append({"src": row.src, "id": row.id})
return result
def outputs_derived_from_input_hda(self, input_hda_id):
def outputs_derived_from_input_hda(self, input_hda_id: int):
hda_select = (
select(
[
@@ -94,7 +94,7 @@ class JobConnectionsManager:
)
return hda_select, hdca_select
def outputs_derived_from_input_hdca(self, input_hdca_id):
def outputs_derived_from_input_hdca(self, input_hdca_id: int):
hda_select = (
select(
[
@@ -126,7 +126,7 @@ class JobConnectionsManager:
)
return hda_select, hdca_select
def inputs_for_hda(self, input_hda_id):
def inputs_for_hda(self, input_hda_id: int):
input_hdas = (
select(
[
@@ -157,7 +157,7 @@ class JobConnectionsManager:
)
return input_hdas, input_hdcas
def inputs_for_hdca(self, input_hdca_id):
def inputs_for_hdca(self, input_hdca_id: int):
input_hdas = (
select(
[
@@ -737,6 +737,38 @@ class TestHistoryContentsApi(ApiTestCase):
).json()
assert len(contents_response) == 0
@skip_without_tool("cat_data_and_sleep")
def test_index_filter_by_related_items(self, history_id):
# initialise history with 2 datasets
input_hda_id = self.dataset_populator.new_dataset(history_id)["id"]
unrelated_hid = self.dataset_populator.new_dataset(history_id)["hid"]
# Run tool on first dataset to get 3rd, related dataset
inputs = {
"input1": {"src": "hda", "id": input_hda_id},
"sleep_time": 0,
}
run_response = self.dataset_populator.run_tool_raw(
"cat_data_and_sleep",
inputs,
history_id,
)
related_hid = run_response.json()["outputs"][0]["hid"]
# Test q = related-eq, for related items
contents_response = self._get(f"histories/{history_id}/contents?v=dev&q=related-eq&qv={related_hid}").json()
assert len(contents_response) == 2
# Test q = related, for unrelated item
contents_response = self._get(f"histories/{history_id}/contents?v=dev&q=related&qv={unrelated_hid}").json()
assert len(contents_response) == 1
# Test error case: qv is string
related_qv = "one"
contents_response = self._get(f"histories/{history_id}/contents?v=dev&q=related-eq&qv={related_qv}")
assert contents_response.status_code == 400
assert contents_response.json()["err_msg"] == "unparsable value for related filter"
def test_elements_datatypes_field(self, history_id):
collection_name = "homogeneous"
expected_datatypes = ["txt"]