Skip to content

Commit 27803e2

Browse files
committed
Allow filtering job searches by history ID
Fix the following test failure in galaxyproject#19112 : ``` FAILED lib/galaxy_test/api/test_jobs.py::TestJobsApi::test_delete_job - assert 1 == 0 + where 1 = len([{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}]) + where [{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}] = json() + where json = <Response [200]>.json ``` where searching for jobs has now started returning jobs with the same tool_id and input hashes but from different histories.
1 parent dcf73d5 commit 27803e2

File tree

5 files changed

+32
-7
lines changed

5 files changed

+32
-7
lines changed

client/src/api/schema/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20056,6 +20056,11 @@ export interface components {
2005620056
};
2005720057
/** SearchJobsPayload */
2005820058
SearchJobsPayload: {
20059+
/**
20060+
* History ID
20061+
* @description The encoded ID of the history associated with this job.
20062+
*/
20063+
history_id?: string | null;
2005920064
/**
2006020065
* Inputs
2006120066
* @description The inputs of the job.

lib/galaxy/managers/jobs.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ def by_tool_input(
445445
param: ToolStateJobInstancePopulatedT,
446446
param_dump: ToolStateDumpedToJsonInternalT,
447447
job_state: Optional[JobStatesT] = (Job.states.OK,),
448+
history_id: Union[int, None] = None,
448449
require_name_match: bool = True,
449450
):
450451
"""Search for jobs producing same results using the 'inputs' part of a tool POST."""
@@ -489,6 +490,7 @@ def populate_input_data_input_id(path, key, value):
489490
job_state=job_state,
490491
param_dump=param_dump,
491492
wildcard_param_dump=wildcard_param_dump,
493+
history_id=history_id,
492494
require_name_match=require_name_match,
493495
)
494496

@@ -501,6 +503,7 @@ def __search(
501503
job_state: Optional[JobStatesT],
502504
param_dump: ToolStateDumpedToJsonInternalT,
503505
wildcard_param_dump=None,
506+
history_id: Union[int, None] = None,
504507
require_name_match: bool = True,
505508
):
506509
search_timer = ExecutionTimer()
@@ -566,7 +569,7 @@ def replace_dataset_ids(path, key, value):
566569
return None
567570

568571
stmt = stmt.where(*data_conditions).group_by(model.Job.id, *used_ids)
569-
stmt = self._filter_jobs(stmt, tool_id, user.id, tool_version, job_state, wildcard_param_dump)
572+
stmt = self._filter_jobs(stmt, tool_id, user.id, tool_version, job_state, wildcard_param_dump, history_id)
570573
stmt = self._exclude_jobs_with_deleted_outputs(stmt)
571574

572575
for job in self.sa_session.execute(stmt):
@@ -634,8 +637,15 @@ def replace_dataset_ids(path, key, value):
634637
return None
635638

636639
def _filter_jobs(
637-
self, stmt, tool_id: str, user_id: int, tool_version: Optional[str], job_state, wildcard_param_dump
638-
):
640+
self,
641+
stmt: "Select[tuple[int]]",
642+
tool_id: str,
643+
user_id: int,
644+
tool_version: Optional[str],
645+
job_state: Union[JobStatesT, None],
646+
wildcard_param_dump,
647+
history_id: Union[int, None],
648+
) -> "Select[tuple[int]]":
639649
"""Build subquery that selects a job with correct job parameters."""
640650
job_ids_materialized_cte = stmt.cte("job_ids_cte")
641651
outer_select_columns = [job_ids_materialized_cte.c[col.name] for col in stmt.selected_columns]
@@ -657,6 +667,9 @@ def _filter_jobs(
657667
if tool_version:
658668
stmt = stmt.where(Job.tool_version == str(tool_version))
659669

670+
if history_id is not None:
671+
stmt = stmt.where(Job.history_id == history_id)
672+
660673
if job_state is None:
661674
job_states: set[str] = {
662675
Job.states.NEW,

lib/galaxy/schema/jobs.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ class SearchJobsPayload(Model):
126126
title="State",
127127
description="Current state of the job.",
128128
)
129+
history_id: Union[DecodedDatabaseIdField, None] = Field(
130+
default=None,
131+
title="History ID",
132+
description="The encoded ID of the history associated with this job.",
133+
)
129134
model_config = ConfigDict(extra="allow") # This is used for items named file_ and __file_
130135

131136
@field_validator("inputs", mode="before")

lib/galaxy/webapps/galaxy/api/jobs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ def search(
598598
param=param,
599599
param_dump=param_dump,
600600
job_state=payload.state,
601+
history_id=payload.history_id,
601602
)
602603
if job:
603604
jobs.append(job)

lib/galaxy_test/api/test_jobs.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import time
55
import urllib.parse
66
from operator import itemgetter
7+
from typing import Union
78
from unittest import SkipTest
89

910
import pytest
@@ -903,9 +904,7 @@ def test_search_with_hdca_pair_input(self, history_id):
903904
"f2": {"src": "hdca", "id": new_list_a},
904905
}
905906
)
906-
search_payload = self._search_payload(
907-
history_id=new_history_id, tool_id="multi_data_param", inputs=copied_inputs
908-
)
907+
search_payload = self._search_payload(tool_id="multi_data_param", inputs=copied_inputs)
909908
self._search(search_payload, expected_search_count=1)
910909
# Now we delete the original input HDCA that was used -- we should still be able to find the job
911910
delete_response = self._delete(f"histories/{history_id}/contents/dataset_collections/{list_id_a}")
@@ -1150,7 +1149,9 @@ def _job_search(self, tool_id, history_id, inputs):
11501149
self._search(search_payload, expected_search_count=1)
11511150
return tool_response
11521151

1153-
def _search_payload(self, history_id, tool_id, inputs, state="ok"):
1152+
def _search_payload(
1153+
self, tool_id: str, inputs: str, state: str = "ok", history_id: Union[str, None] = None
1154+
) -> dict[str, Union[str, None]]:
11541155
search_payload = dict(tool_id=tool_id, inputs=inputs, history_id=history_id, state=state)
11551156
return search_payload
11561157

0 commit comments

Comments
 (0)