diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue
index 6025c523050c..71983b2421d0 100644
--- a/client/src/components/Workflow/Editor/Index.vue
+++ b/client/src/components/Workflow/Editor/Index.vue
@@ -355,6 +355,15 @@ export default {
}
const tags = ref([]);
+
+ watch(
+ () => props.workflowTags,
+ (newTags) => {
+ tags.value = [...newTags];
+ },
+ { immediate: true }
+ );
+
const setTagsHandler = new SetValueActionHandler(
undoRedoStore,
(value) => (tags.value = structuredClone(value)),
@@ -792,12 +801,9 @@ export default {
this.report.markdown = markdown;
},
onRun() {
- const runUrl = `/workflows/run?id=${this.id}${
- this.version !== undefined ? `&version=${this.version}` : ""
- }`;
- this.onNavigate(runUrl);
+ this.onNavigate(`/workflows/run?id=${this.id}`, false, false, true);
},
- async onNavigate(url, forceSave = false, ignoreChanges = false) {
+ async onNavigate(url, forceSave = false, ignoreChanges = false, appendVersion = false) {
if (this.isNewTempWorkflow) {
await this.onCreate();
} else if (this.hasChanges && !forceSave && !ignoreChanges) {
@@ -810,6 +816,9 @@ export default {
await this.onSave();
}
+ if (appendVersion && this.version !== undefined) {
+ url += `&version=${this.version}`;
+ }
this.hasChanges = false;
await nextTick();
this.$router.push(url);
diff --git a/client/src/components/Workflow/Editor/SaveChangesModal.vue b/client/src/components/Workflow/Editor/SaveChangesModal.vue
index 9a2b5bbda2d0..921fa76a4b9e 100644
--- a/client/src/components/Workflow/Editor/SaveChangesModal.vue
+++ b/client/src/components/Workflow/Editor/SaveChangesModal.vue
@@ -24,7 +24,7 @@ const busy = ref(false);
const emit = defineEmits<{
/** Proceed with or without saving the changes */
- (e: "on-proceed", url: string, forceSave: boolean, ignoreChanges: boolean): void;
+ (e: "on-proceed", url: string, forceSave: boolean, ignoreChanges: boolean, appendVersion: boolean): void;
/** Update the nav URL prop */
(e: "update:nav-url", url: string): void;
/** Update the show modal boolean prop */
@@ -49,13 +49,13 @@ function closeModal() {
function dontSave() {
busy.value = true;
- emit("on-proceed", props.navUrl, false, true);
+ emit("on-proceed", props.navUrl, false, true, true);
}
function saveChanges() {
busy.value = true;
closeModal();
- emit("on-proceed", props.navUrl, true, false);
+ emit("on-proceed", props.navUrl, true, false, true);
}
diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue
index 4678418e6f5d..243c44d428d1 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue
@@ -74,7 +74,7 @@
+ :parameters="[getParamInput(stepDetails)]" />
({
invocationId: route.params.invocationId,
isFullPage: true,
- fromPanel: route.query.from_panel,
+ fromPanel: Boolean(route.query.from_panel),
}),
},
{
diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index 07a2eadd6f40..d2d9b0fcfba2 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -8922,7 +8922,7 @@ def to_dict(self, view="collection", value_mapper=None, step_details=False, lega
for input_step_parameter in self.input_step_parameters:
label = input_step_parameter.workflow_step.label
if not label:
- continue
+ label = f"{input_step_parameter.workflow_step.order_index + 1}: Unnamed parameter"
input_parameters[label] = {
"parameter_value": input_step_parameter.parameter_value,
"label": label,
diff --git a/lib/galaxy/model/store/discover.py b/lib/galaxy/model/store/discover.py
index 212515d230fc..2fb7f386be7e 100644
--- a/lib/galaxy/model/store/discover.py
+++ b/lib/galaxy/model/store/discover.py
@@ -38,6 +38,10 @@
from galaxy.util.hash_util import HASH_NAME_MAP
if TYPE_CHECKING:
+ from galaxy.job_execution.output_collect import (
+ DatasetCollector,
+ ToolMetadataDatasetCollector,
+ )
from galaxy.model.store import ModelExportStore
log = logging.getLogger(__name__)
@@ -50,7 +54,7 @@ class MaxDiscoveredFilesExceededError(ValueError):
pass
-CollectorT = Any # TODO: setup an interface for these file collectors data classes.
+CollectorT = Union["DatasetCollector", "ToolMetadataDatasetCollector"]
class ModelPersistenceContext(metaclass=abc.ABCMeta):
@@ -1058,19 +1062,21 @@ def name(self):
return self.as_dict.get("name")
@property
- def dbkey(self):
- return self.as_dict.get("dbkey", getattr(self.collector, "default_dbkey", "?"))
+ def dbkey(self) -> str:
+ return self.as_dict.get("dbkey", self.collector and self.collector.default_dbkey or "?")
@property
- def ext(self):
- return self.as_dict.get("ext", getattr(self.collector, "default_ext", "data"))
+ def ext(self) -> str:
+ return self.as_dict.get("ext", self.collector and self.collector.default_ext or "data")
@property
- def visible(self):
+ def visible(self) -> bool:
try:
return self.as_dict["visible"].lower() == "visible"
except KeyError:
- return getattr(self.collector, "default_visible", True)
+ if self.collector and self.collector.default_visible is not None:
+ return self.collector.default_visible
+ return True
@property
def link_data(self):
diff --git a/lib/galaxy/tool_util/parser/output_collection_def.py b/lib/galaxy/tool_util/parser/output_collection_def.py
index 1e80ba1ea950..baf61c520cdb 100644
--- a/lib/galaxy/tool_util/parser/output_collection_def.py
+++ b/lib/galaxy/tool_util/parser/output_collection_def.py
@@ -34,7 +34,13 @@ def dataset_collector_descriptions_from_elem(elem, legacy=True):
if num_discover_dataset_blocks == 0 and legacy:
collectors = [DEFAULT_DATASET_COLLECTOR_DESCRIPTION]
else:
- collectors = [dataset_collection_description(**e.attrib) for e in primary_dataset_elems]
+ default_format = elem.attrib.get("format")
+ collectors = []
+ for e in primary_dataset_elems:
+ description_attributes = e.attrib
+ if default_format and "format" not in description_attributes and "ext" not in description_attributes:
+ description_attributes["format"] = default_format
+ collectors.append(dataset_collection_description(**description_attributes))
return _validate_collectors(collectors)
diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd
index 39d07f0cc8b2..73558250251f 100644
--- a/lib/galaxy/tool_util/xsd/galaxy.xsd
+++ b/lib/galaxy/tool_util/xsd/galaxy.xsd
@@ -5641,11 +5641,13 @@ The default is ``galaxy.json``.
- The short name for the output datatype.
-The valid values for format can be found in
+
+(e.g. ``format="pdf"`` or ``format="fastqsanger"``). For collections this is the default
+format for all included elements. Note that the format specified here is ignored for
+discovered data sets on Galaxy versions prior to 24.0 and should be specified using the ```` tag set.
+ ]]>
diff --git a/test/functional/tools/discover_default_ext.xml b/test/functional/tools/discover_default_ext.xml
new file mode 100644
index 000000000000..7f7c7d83cdd8
--- /dev/null
+++ b/test/functional/tools/discover_default_ext.xml
@@ -0,0 +1,52 @@
+
+ 1.txt;
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/functional/tools/sample_tool_conf.xml b/test/functional/tools/sample_tool_conf.xml
index b58a2ab1f64f..45eee3cc16c9 100644
--- a/test/functional/tools/sample_tool_conf.xml
+++ b/test/functional/tools/sample_tool_conf.xml
@@ -208,6 +208,7 @@
+
diff --git a/test/unit/app/tools/test_collect_primary_datasets.py b/test/unit/app/tools/test_collect_primary_datasets.py
index 16f3c6aae13e..fb612853b4db 100644
--- a/test/unit/app/tools/test_collect_primary_datasets.py
+++ b/test/unit/app/tools/test_collect_primary_datasets.py
@@ -128,6 +128,20 @@ def test_collect_multiple_recurse_dict(self):
created_hda_3 = datasets[DEFAULT_TOOL_OUTPUT]["test3"]
assert_created_with_path(self.app.object_store, created_hda_3.dataset, path3)
+ def test_collect_collection_default_format(self):
+ self._replace_output_collectors(
+ """
+
+ """
+ )
+ self._setup_extra_file(subdir="subdir_for_name_discovery", filename="test1")
+ self._setup_extra_file(subdir="subdir_for_name_discovery", filename="test2")
+
+ datasets = self._collect()
+ assert DEFAULT_TOOL_OUTPUT in datasets
+ for dataset in datasets[DEFAULT_TOOL_OUTPUT].values():
+ assert dataset.ext == "abcdef"
+
def test_collect_sorted_reverse(self):
self._replace_output_collectors(
"""