From 7239cf784b1b538c88cd01f63b7f805d45f855bf Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Wed, 8 Oct 2025 10:51:39 +0200 Subject: [PATCH 01/15] require OK datasets for filtering empty datasets fixes: https://github.com/galaxyproject/galaxy/issues/20973 guess this broke here: https://github.com/galaxyproject/galaxy/pull/15892 --- lib/galaxy/tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index ad481fb9c4e..acfa29de411 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -4095,7 +4095,7 @@ class KeepSuccessDatasetsTool(FilterDatasetsTool): class FilterEmptyDatasetsTool(FilterDatasetsTool): tool_type = "filter_empty_datasets_collection" - require_dataset_ok = False + require_dataset_ok = True @staticmethod def element_is_valid(element: model.DatasetCollectionElement): From fcc90c7b1bba6608d16d3ef10f3fe135e92ba45c Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:31:07 +0100 Subject: [PATCH 02/15] Handles optional workflow parameter outputs gracefully on rocrate export Prevents errors when an output value for a workflow step is missing or None, such as for optional parameters not provided by the user. --- lib/galaxy/model/store/ro_crate_utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/store/ro_crate_utils.py b/lib/galaxy/model/store/ro_crate_utils.py index 74a5b7caf5a..e87edacfd22 100644 --- a/lib/galaxy/model/store/ro_crate_utils.py +++ b/lib/galaxy/model/store/ro_crate_utils.py @@ -525,6 +525,12 @@ class WorkflowRunCrateProfileBuilder: ) ) crate.mainEntity.append_to("input", formal_param) + + # Handle case where output_value is None (e.g., optional parameter not provided) + output_value = None + if step.output_value: + output_value = step.output_value.value + return crate.add( ContextEntity( crate, @@ -532,7 +538,7 @@ class WorkflowRunCrateProfileBuilder: properties={ "@type": "PropertyValue", "name": f"{param_id}", - "value": step.output_value.value, + "value": output_value, "exampleOfWork": {"@id": formal_param.id}, }, ) From 5f92026d9896bce79a92b07e92128fa5f5c65a26 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:31:40 +0100 Subject: [PATCH 03/15] Adds test for RO-Crate export with unset optional parameter Ensures exporting a workflow invocation with an optional text parameter left unset does not raise errors, addressing a previous issue with handling None output values during RO-Crate creation. --- test/integration/test_workflow_tasks.py | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/integration/test_workflow_tasks.py b/test/integration/test_workflow_tasks.py index 2c0779d49d0..4fd3c6549c9 100644 --- a/test/integration/test_workflow_tasks.py +++ b/test/integration/test_workflow_tasks.py @@ -74,6 +74,54 @@ class TestWorkflowTasksIntegration(PosixFileSourceSetup, IntegrationTestCase, Us bco = json.load(f) self.workflow_populator.validate_biocompute_object(bco) + def test_export_ro_crate_with_optional_parameter_without_value(self): + """Test exporting invocation with optional text parameter that has no value. + + This tests the fix for the bug where step.output_value is None for optional + parameters that weren't provided, which caused AttributeError when creating RO-Crate. + """ + with self.dataset_populator.test_history() as history_id: + summary = self._run_workflow_with_optional_parameter_without_value(history_id) + invocation_id = summary.invocation_id + + # Export to RO-Crate - this should succeed without AttributeError + ro_crate_path = self.workflow_populator.download_invocation_to_store(invocation_id, extension="rocrate.zip") + + # Verify the RO-Crate was created successfully + with CompressedFile(ro_crate_path) as cf: + assert cf.file_type == "zip" + + def _run_workflow_with_optional_parameter_without_value(self, history_id: str) -> RunJobsSummary: + """Run a workflow with an optional text parameter that is not provided.""" + workflow = """ +class: GalaxyWorkflow +inputs: + input_data: + type: data + optional_text_param: + type: text + optional: true +steps: + cat_step: + tool_id: cat + in: + input1: input_data +outputs: + output_data: + outputSource: cat_step/out_file1 +""" + test_data = """ +input_data: + value: 1.bed + type: File +""" + summary = self.workflow_populator.run_workflow( + workflow, + test_data=test_data, + history_id=history_id, + ) + return summary + def _export_invocation_to_format(self, extension: str, to_uri: bool): with self.dataset_populator.test_history() as history_id: summary = self._run_workflow_with_runtime_data_column_parameter(history_id) From fc1150da70f690f730e605100b82c59a93f9ccb4 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 29 Oct 2025 11:43:30 -0400 Subject: [PATCH 04/15] Fix client side error handling when uploading fetch workbooks to the rule builder. --- .../components/Collections/BuildFileSetWizard.vue | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/client/src/components/Collections/BuildFileSetWizard.vue b/client/src/components/Collections/BuildFileSetWizard.vue index 18327ecbb99..c4f2d46b66f 100644 --- a/client/src/components/Collections/BuildFileSetWizard.vue +++ b/client/src/components/Collections/BuildFileSetWizard.vue @@ -1,5 +1,5 @@ @@ -11,6 +21,8 @@ const emit = defineEmits(["download"]); Step 1: Download - Download workbook. + Download workbook. From ac50de54f62346977bdf6bc3043925b16a372e31 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 29 Oct 2025 12:03:48 -0400 Subject: [PATCH 06/15] We updated this to support many more kinds of tabular datasets. --- .../components/Collections/wizard/HiddenWorkbookUploadInput.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Collections/wizard/HiddenWorkbookUploadInput.vue b/client/src/components/Collections/wizard/HiddenWorkbookUploadInput.vue index 0e54ba459dc..8760e308d63 100644 --- a/client/src/components/Collections/wizard/HiddenWorkbookUploadInput.vue +++ b/client/src/components/Collections/wizard/HiddenWorkbookUploadInput.vue @@ -19,6 +19,6 @@ defineExpose({ From ded7f0b567e3b12a43686614846f2212e21ad4d8 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 30 Oct 2025 10:20:20 -0400 Subject: [PATCH 07/15] Try to improve error messages for workbook upload parsing problems. --- .../dataset_collections/workbook_util.py | 21 +++++++++++------ .../dataset_collections/test_workbook_util.py | 23 ++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/galaxy/model/dataset_collections/workbook_util.py b/lib/galaxy/model/dataset_collections/workbook_util.py index ffa43770b96..e687dfa3183 100644 --- a/lib/galaxy/model/dataset_collections/workbook_util.py +++ b/lib/galaxy/model/dataset_collections/workbook_util.py @@ -235,17 +235,24 @@ def load_workbook_from_base64(content: str) -> ReadOnlyWorkbook: is_excel = file_like.read(4) == b"\x50\x4b\x03\x04" workbook: ReadOnlyWorkbook file_like.seek(0) - try: - if is_excel: + if is_excel: + try: workbook = ExcelReadOnlyWorkbook(load_workbook(file_like, data_only=True)) - else: + except Exception as e: + extra_message = str(e) + raise RequestParameterInvalidException( + f"The provided content is not a valid Excel file (or at least not one Galaxy knows how to parse). Please check the content and try again. The underlying error was [{extra_message}]" + ) + else: + try: tabular = decoded_content.decode("utf-8") file_like_as_utf8 = StringIO(tabular) workbook = CsvReaderReadOnlyWorkbook(file_like_as_utf8) - except Exception: - raise RequestParameterInvalidException( - "The provided content is not a valid Excel file. Please check the content and try again." - ) + except Exception as e: + extra_message = str(e) + raise RequestParameterInvalidException( + f"The provided content is not a parsable as a valid CSV or TSV (or at least not one Galaxy knows how to parse). Please check the content and try again. The underlying error was [{extra_message}]" + ) return workbook diff --git a/test/unit/data/dataset_collections/test_workbook_util.py b/test/unit/data/dataset_collections/test_workbook_util.py index 5d58c0f06b4..7e0df29aad1 100644 --- a/test/unit/data/dataset_collections/test_workbook_util.py +++ b/test/unit/data/dataset_collections/test_workbook_util.py @@ -1,4 +1,10 @@ -from galaxy.model.dataset_collections.workbook_util import index_to_excel_column +import base64 + +from galaxy.model.dataset_collections.workbook_util import ( + index_to_excel_column, + load_workbook_from_base64, +) +from galaxy.util.resources import resource_path def test_index_to_excel_column(): @@ -8,3 +14,18 @@ def test_index_to_excel_column(): assert index_to_excel_column(700) == "ZY" assert index_to_excel_column(701) == "ZZ" assert index_to_excel_column(702) == "AAA" + + +def test_load_workbook_from_base64(): + workbook_base64 = resource_path_to_base64("filled_in_workbook_1.xlsx") + workbook = load_workbook_from_base64(workbook_base64) + assert workbook is not None + + workbook_base64 = resource_path_to_base64("filled_in_workbook_1.tsv") + workbook = load_workbook_from_base64(workbook_base64) + assert workbook is not None + + +def resource_path_to_base64(resource_name: str) -> str: + resource_bytes = resource_path("galaxy.model.unittest_utils", resource_name).read_bytes() + return base64.b64encode(resource_bytes).decode("utf-8") From 2a89493090fbcc2e0254d16caddebb2242e93257 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 30 Oct 2025 10:37:04 -0400 Subject: [PATCH 08/15] Grammar fix. Thanks to https://github.com/galaxyproject/galaxy/issues/21098. --- client/src/components/Collections/BuildFileSetWizard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Collections/BuildFileSetWizard.vue b/client/src/components/Collections/BuildFileSetWizard.vue index c4f2d46b66f..8cf99aa9355 100644 --- a/client/src/components/Collections/BuildFileSetWizard.vue +++ b/client/src/components/Collections/BuildFileSetWizard.vue @@ -113,7 +113,7 @@ const wizard = useWizard({ }, "upload-workbook": { label: "Upload workbook", - instructions: "Upload a workbook containing with URIs and metadata", + instructions: "Upload a workbook containing URIs with metadata", isValid: () => sourceFrom.value === "workbook" && workbookCompleted.value, isSkippable: () => sourceFrom.value !== "workbook", }, From 45c6dee5ffaeff79b541c38d933a99e59374f249 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 30 Oct 2025 10:57:20 -0400 Subject: [PATCH 09/15] Make workbook upload button a bit more intuitive. It didn't hover and the mouse cursor wouldn't change - I think wrapping it in an anchor tag and adding a hover effect fixes this. Thanks to the PR review on https://github.com/galaxyproject/galaxy/issues/21098. --- .../Collections/BuildFileSetWizard.vue | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/client/src/components/Collections/BuildFileSetWizard.vue b/client/src/components/Collections/BuildFileSetWizard.vue index 8cf99aa9355..e575912eb85 100644 --- a/client/src/components/Collections/BuildFileSetWizard.vue +++ b/client/src/components/Collections/BuildFileSetWizard.vue @@ -271,15 +271,16 @@ const {

{{ title }} - + + +

@@ -346,5 +347,8 @@ const { // modeled a bit after upload-helper in the upload component... .workbook-upload-helper { color: $border-color; + &:hover { + color: $brand-primary; + } } From d8bcf41b9750661b968205f8cc5b5e7ac5fa4d94 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 30 Oct 2025 11:05:10 -0400 Subject: [PATCH 10/15] Column names fixes for forthcoming workbook tutorial. --- client/src/components/Collections/wizard/fetchWorkbooks.ts | 2 ++ .../rule_target_column_specification.yml | 7 +++++++ .../model/dataset_collections/rule_target_columns.py | 2 ++ 3 files changed, 11 insertions(+) diff --git a/client/src/components/Collections/wizard/fetchWorkbooks.ts b/client/src/components/Collections/wizard/fetchWorkbooks.ts index 774e1668f8b..cc972662ed9 100644 --- a/client/src/components/Collections/wizard/fetchWorkbooks.ts +++ b/client/src/components/Collections/wizard/fetchWorkbooks.ts @@ -88,7 +88,9 @@ const COLUMN_TITLE_PREFIXES: Record = { deferredurl: "url_deferred", genome: "dbkey", dbkey: "dbkey", + build: "dbkey", filetype: "file_type", + type: "file_type", extension: "file_type", info: "info", tag: "tags", diff --git a/lib/galaxy/model/dataset_collections/rule_target_column_specification.yml b/lib/galaxy/model/dataset_collections/rule_target_column_specification.yml index ab4c4ff981d..8df4d656155 100644 --- a/lib/galaxy/model/dataset_collections/rule_target_column_specification.yml +++ b/lib/galaxy/model/dataset_collections/rule_target_column_specification.yml @@ -48,6 +48,10 @@ - doc: "dbkey maps to the dbkey target type" column_header: "dbkey" maps_to: "dbkey" +- doc: "build maps to the dbkey target type" + column_header: "build" + maps_to: "dbkey" + - doc: "filetype maps to the file_type target type" column_header: "file type" @@ -55,6 +59,9 @@ - doc: "extension maps to the file_type target type" column_header: "extension" maps_to: "file_type" +- doc: "Type maps to the file_type target type" + column_header: "Type" + maps_to: "file_type" - doc: "info maps to the info target type" column_header: "info" diff --git a/lib/galaxy/model/dataset_collections/rule_target_columns.py b/lib/galaxy/model/dataset_collections/rule_target_columns.py index ee731311e18..0737ae27555 100644 --- a/lib/galaxy/model/dataset_collections/rule_target_columns.py +++ b/lib/galaxy/model/dataset_collections/rule_target_columns.py @@ -23,9 +23,11 @@ COLUMN_TITLE_PREFIXES: dict[str, RuleBuilderMappingTargetKey] = { "genome": "dbkey", "dbkey": "dbkey", "genomebuild": "dbkey", + "build": "dbkey", "filetype": "file_type", "extension": "file_type", "fileextension": "file_type", + "type": "file_type", "info": "info", "tag": "tags", "grouptag": "group_tags", From 34d62143386e4b5fe0f31fe5b8883b616c43eca4 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 29 Oct 2025 11:04:54 -0400 Subject: [PATCH 11/15] Tighter API for tool run tagging. Should deal with https://github.com/galaxyproject/galaxy/issues/20961. --- client/src/components/Tool/ToolForm.vue | 2 +- lib/galaxy/tools/__init__.py | 2 +- lib/galaxy/webapps/galaxy/services/tools.py | 2 ++ lib/galaxy_test/api/test_tool_output_tagging.py | 2 -- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/components/Tool/ToolForm.vue b/client/src/components/Tool/ToolForm.vue index ebaa917b9a0..e4546fb1fce 100644 --- a/client/src/components/Tool/ToolForm.vue +++ b/client/src/components/Tool/ToolForm.vue @@ -395,9 +395,9 @@ export default { tool_id: this.formConfig.id, tool_version: this.formConfig.version, tool_uuid: this.toolUuid, + __tags: this.tags, inputs: { ...this.formData, - __tags: this.tags, }, }; if (this.useEmail) { diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 1e3c9971759..0734829b2cf 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -2203,6 +2203,7 @@ class Tool(UsesDictVisibleKeys, ToolParameterBundle): preferred_object_store_id: Optional[str] = DEFAULT_PREFERRED_OBJECT_STORE_ID, credentials_context: Optional[CredentialsContext] = None, input_format: InputFormatT = "legacy", + tags: Optional[list[str]] = None, ): """ Process incoming parameters for this tool from the dict `incoming`, @@ -2236,7 +2237,6 @@ class Tool(UsesDictVisibleKeys, ToolParameterBundle): # Reserved global tags parameter. Applies to all tool outputs. # This may change in the future if per-output tags are introduced. - tags = incoming.get("__tags", []) if tags: tag_handler = trans.tag_handler for _, hda in execution_tracker.output_datasets: diff --git a/lib/galaxy/webapps/galaxy/services/tools.py b/lib/galaxy/webapps/galaxy/services/tools.py index 377cd8f23a6..700a5fbb786 100644 --- a/lib/galaxy/webapps/galaxy/services/tools.py +++ b/lib/galaxy/webapps/galaxy/services/tools.py @@ -331,6 +331,7 @@ class ToolsService(ServiceBase): input_format = cast(Literal["legacy", "21.01"], input_format) if "data_manager_mode" in payload: incoming["__data_manager_mode"] = payload["data_manager_mode"] + tags = payload.get("__tags") vars = tool.handle_input( trans, incoming, @@ -339,6 +340,7 @@ class ToolsService(ServiceBase): input_format=input_format, preferred_object_store_id=preferred_object_store_id, credentials_context=CredentialsContext(root=credentials_context) if credentials_context else None, + tags=tags, ) new_pja_flush = False diff --git a/lib/galaxy_test/api/test_tool_output_tagging.py b/lib/galaxy_test/api/test_tool_output_tagging.py index 228849e214a..206d16648b8 100644 --- a/lib/galaxy_test/api/test_tool_output_tagging.py +++ b/lib/galaxy_test/api/test_tool_output_tagging.py @@ -27,7 +27,6 @@ class TestToolOutputTaggingApi(ApiTestCase): "history_id": history_id, "inputs": { "input1": {"values": [{"src": "hda", "id": hda["id"]}]}, - "__tags": ["t1", "t2"], }, "input_format": "21.01", "__tags": ["t1", "t2"], @@ -54,7 +53,6 @@ class TestToolOutputTaggingApi(ApiTestCase): "batch": True, "values": [{"src": "hdca", "id": hdca["id"]}], }, - "__tags": ["m1", "m2"], }, "input_format": "21.01", "__tags": ["m1", "m2"], From 12b8f8fb33d313f40a93d606b1d690a121cf183d Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Thu, 30 Oct 2025 17:16:39 -0500 Subject: [PATCH 12/15] [25.1] Wrap ontology card in tool discovery view below list view selector --- client/src/components/ToolsList/ToolsList.vue | 72 ++--- .../ToolsList/ToolsListSectionFilters.vue | 245 +++++++++--------- 2 files changed, 165 insertions(+), 152 deletions(-) diff --git a/client/src/components/ToolsList/ToolsList.vue b/client/src/components/ToolsList/ToolsList.vue index 94bb9e0da44..d210eecd625 100644 --- a/client/src/components/ToolsList/ToolsList.vue +++ b/client/src/components/ToolsList/ToolsList.vue @@ -257,43 +257,43 @@ function onToggleView(newView: ListViewMode) { -
- + + +
diff --git a/client/src/components/ToolsList/ToolsListSectionFilters.vue b/client/src/components/ToolsList/ToolsListSectionFilters.vue index 909afd4398d..32aa056eac5 100644 --- a/client/src/components/ToolsList/ToolsListSectionFilters.vue +++ b/client/src/components/ToolsList/ToolsListSectionFilters.vue @@ -99,134 +99,147 @@ function searchWithinSections(sections: ToolSection[], query: string) {