Skip to content

Commit 79e3440

Browse files
committed
Merge branch 'release_25.1' into dev
2 parents cf7da4d + 2bed907 commit 79e3440

File tree

10 files changed

+74
-147
lines changed

10 files changed

+74
-147
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,13 @@ skip-client: ## Run only the server, skipping the client build.
180180

181181
node-deps: ## Install NodeJS dependencies.
182182
ifndef YARN
183-
npm install -g yarn;
183+
corepack enable yarn;
184184
endif
185185
$(IN_VENV) yarn install $(YARN_INSTALL_OPTS)
186186

187187
client-node-deps: ## Install NodeJS dependencies for the client.
188188
ifndef YARN
189-
npm install -g yarn;
189+
corepack enable yarn;
190190
endif
191191
$(IN_VENV) cd client && yarn install $(YARN_INSTALL_OPTS)
192192

client/src/components/Workflow/Editor/SVGConnection.vue

Lines changed: 11 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
<script setup lang="ts">
22
import { curveBasis, line } from "d3";
3-
import { computed } from "vue";
3+
import { computed, type PropType } from "vue";
44
55
import { useWorkflowStores } from "@/composables/workflowStores";
66
import { getConnectionId } from "@/stores/workflowConnectionStore";
77
import type { TerminalPosition } from "@/stores/workflowEditorStateStore";
88
import type { Connection } from "@/stores/workflowStoreTypes";
99
10-
interface Props {
11-
id: string;
12-
connection: Connection;
13-
terminalPosition?: TerminalPosition | null;
14-
flowing?: boolean;
15-
breathing?: boolean;
16-
}
17-
18-
const props = withDefaults(defineProps<Props>(), {
19-
terminalPosition: null,
20-
flowing: false,
21-
breathing: false,
10+
const props = defineProps({
11+
id: String,
12+
connection: {
13+
type: Object as PropType<Connection>,
14+
required: true,
15+
},
16+
terminalPosition: {
17+
type: Object as PropType<TerminalPosition | null>,
18+
default: null,
19+
},
2220
});
2321
2422
const ribbonMargin = 4;
@@ -166,24 +164,6 @@ const paths = computed(() => {
166164
return lines.map((l) => curve(l)!);
167165
});
168166
169-
// Estimate path length for animation timing on flowing connections
170-
const estimatedPathLength = computed(() => {
171-
if (!props.flowing) {
172-
return 0;
173-
}
174-
175-
if (!connectionPosition.value) {
176-
return 100;
177-
}
178-
179-
const deltaX = connectionPosition.value.endX - connectionPosition.value.startX;
180-
const deltaY = connectionPosition.value.endY - connectionPosition.value.startY;
181-
const straightDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
182-
183-
// Add some extra length to account for curves
184-
return Math.max(100, straightDistance * 1.3);
185-
});
186-
187167
const lineWidth = computed(() => {
188168
if (inputIsMappedOver.value || outputIsMappedOver.value) {
189169
return 2;
@@ -203,10 +183,6 @@ const connectionClass = computed(() => {
203183
classList.push("invalid");
204184
}
205185
206-
if (props.breathing) {
207-
classList.push("breathing");
208-
}
209-
210186
return classList.join(" ");
211187
});
212188
@@ -253,22 +229,6 @@ function keyForIndex(index: number) {
253229
:stroke-width="lineWidth"
254230
fill="none">
255231
</path>
256-
257-
<template v-if="props.flowing">
258-
<path
259-
v-for="(path, index) in paths"
260-
:key="`particle-${keyForIndex(index)}`"
261-
class="connection-particle"
262-
:d="path"
263-
:stroke-width="lineWidth * 2"
264-
:style="{
265-
'--path-length': `${estimatedPathLength}px`,
266-
'--animation-duration': `${Math.max(2, estimatedPathLength / 80)}s`,
267-
}"
268-
stroke-linecap="round"
269-
fill="none">
270-
</path>
271-
</template>
272232
</g>
273233
</template>
274234

@@ -287,45 +247,6 @@ function keyForIndex(index: number) {
287247
&.invalid {
288248
stroke: #{$brand-warning};
289249
}
290-
291-
&.breathing {
292-
animation: breathe 2s ease-in-out infinite;
293-
}
294-
}
295-
296-
.connection-particle {
297-
stroke: white;
298-
stroke-dasharray: 0 calc(var(--path-length, 100px) / 2) 0 var(--path-length, 100px);
299-
animation: flow var(--animation-duration, 1s) linear infinite;
300-
}
301-
}
302-
303-
@keyframes flow {
304-
0% {
305-
stroke-dashoffset: 0;
306-
}
307-
100% {
308-
stroke-dashoffset: calc(-2 * var(--path-length, 100px) - 8px);
309-
}
310-
}
311-
312-
@keyframes breathe {
313-
0%,
314-
100% {
315-
stroke-width: var(--stroke-width, 4px);
316-
opacity: 0.8;
317-
}
318-
25% {
319-
stroke-width: calc(var(--stroke-width, 4px) * 1.2);
320-
opacity: 0.9;
321-
}
322-
50% {
323-
stroke-width: calc(var(--stroke-width, 4px) * 1.4);
324-
opacity: 1;
325-
}
326-
75% {
327-
stroke-width: calc(var(--stroke-width, 4px) * 1.2);
328-
opacity: 0.9;
329250
}
330251
}
331252
</style>

client/src/components/Workflow/Editor/WorkflowEdges.vue

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,6 @@ const props = defineProps<{
1414
draggingConnection: TerminalPosition | null;
1515
draggingTerminal: OutputTerminals | null;
1616
transform: { x: number; y: number; k: number };
17-
/** Stores the step IDs for steps in the invocation which should have a
18-
* "breathing" or "flowing" animation applied to their connections.
19-
*/
20-
stepConnectionClasses?: {
21-
breathing: number[];
22-
flowing: number[];
23-
};
2417
}>();
2518
2619
const { connectionStore } = useWorkflowStores();
@@ -50,36 +43,20 @@ function key(connection: Connection) {
5043
function id(connection: Connection) {
5144
return `connection-node-${connection.input.stepId}-input-${connection.input.name}-node-${connection.output.stepId}-output-${connection.output.name}`;
5245
}
53-
54-
/** Checks the connection's input and output step IDs to determine if a "breathing"
55-
* or "flowing" class should be applied in the `SVGConnection` component.
56-
*/
57-
function getConnectionState(connection: Connection, stateType: "breathing" | "flowing") {
58-
if (props.stepConnectionClasses) {
59-
return (
60-
props.stepConnectionClasses[stateType].includes(connection.output.stepId) ||
61-
props.stepConnectionClasses[stateType].includes(connection.input.stepId)
62-
);
63-
}
64-
return false;
65-
}
6646
</script>
6747

6848
<template>
6949
<div class="workflow-edges">
7050
<svg class="workflow-edges">
7151
<SVGConnection
7252
v-if="draggingConnection"
73-
id="dragging-connection"
7453
:connection="draggingConnection[0]"
7554
:terminal-position="draggingConnection[1]" />
7655
<SVGConnection
7756
v-for="connection in connections"
7857
:id="id(connection)"
7958
:key="key(connection)"
80-
:connection="connection"
81-
:flowing="getConnectionState(connection, 'flowing')"
82-
:breathing="getConnectionState(connection, 'breathing')" />
59+
:connection="connection" />
8360
</svg>
8461
</div>
8562
</template>

client/src/components/Workflow/Editor/WorkflowGraph.vue

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { storeToRefs } from "pinia";
44
import { computed, type PropType, provide, reactive, type Ref, ref, watch, watchEffect } from "vue";
55
66
import { DatatypesMapperModel } from "@/components/Datatypes/model";
7-
import { isGraphStep } from "@/composables/useInvocationGraph";
87
import { useWorkflowStores } from "@/composables/workflowStores";
98
import type { TerminalPosition, XYPosition } from "@/stores/workflowEditorStateStore";
109
import type { Step } from "@/stores/workflowStepStore";
@@ -73,24 +72,6 @@ const { viewportBoundingBox, updateViewportBaseBoundingBox } = useViewportBoundi
7372
);
7473
const { getWorkflowBoundingBox } = useWorkflowBoundingBox();
7574
76-
const stepConnectionClasses = computed<{
77-
breathing: number[];
78-
flowing: number[];
79-
}>(() => {
80-
const retVal: { breathing: number[]; flowing: number[] } = { breathing: [], flowing: [] };
81-
for (const stepId in props.steps) {
82-
const step = props.steps[stepId];
83-
if (step && isGraphStep(step) && step.state) {
84-
if (["queued", "new", "waiting"].includes(step.state)) {
85-
retVal.breathing.push(step.id);
86-
} else if (step.state === "running") {
87-
retVal.flowing.push(step.id);
88-
}
89-
}
90-
}
91-
return retVal;
92-
});
93-
9475
function fitWorkflow(minimumFitZoom = 0.5, maximumFitZoom = 1.0, padding = 50.0) {
9576
if (!Object.keys(props.steps).length) {
9677
// If there are no steps, we cannot fit the workflow.
@@ -225,8 +206,7 @@ defineExpose({
225206
<WorkflowEdges
226207
:transform="transform"
227208
:dragging-terminal="draggingTerminal"
228-
:dragging-connection="draggingPosition"
229-
:step-connection-classes="stepConnectionClasses" />
209+
:dragging-connection="draggingPosition" />
230210
<WorkflowNode
231211
v-for="(step, key) in steps"
232212
:id="step.id"

client/src/composables/useInvocationGraph.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,3 @@ export function getHeaderClass(state: string) {
366366
[`header-${state}`]: !!state,
367367
};
368368
}
369-
370-
export function isGraphStep(step: Step | GraphStep): step is GraphStep {
371-
return "jobs" in step;
372-
}

lib/galaxy/model/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5170,9 +5170,15 @@ def get_quota_source_label(self):
51705170

51715171
quota_source_label = property(get_quota_source_label)
51725172

5173-
def set_skipped(self, object_store_populator: "ObjectStorePopulator") -> None:
5173+
def set_skipped(self, object_store_populator: "ObjectStorePopulator", replace_dataset: bool) -> None:
51745174
assert self.dataset
51755175
object_store_populator.set_object_store_id(self)
5176+
if replace_dataset:
5177+
replacement = Dataset(state=Dataset.states.NEW)
5178+
replacement.object_store_id = self.dataset.object_store_id
5179+
self.dataset = replacement
5180+
self.dataset_id = None
5181+
self.dataset.object_store.create(self.dataset)
51765182
self.extension = "expression.json"
51775183
self.state = self.states.OK
51785184
self.blurb = "skipped"

lib/galaxy/tools/actions/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ def handle_output(name, output, hidden=None):
741741
hdca.collection.mark_as_populated()
742742
object_store_populator = ObjectStorePopulator(trans.app, trans.user)
743743
for data in out_data.values():
744-
data.set_skipped(object_store_populator)
744+
data.set_skipped(object_store_populator, replace_dataset=False)
745745
job.preferred_object_store_id = preferred_object_store_id
746746
self._handle_credentials_context(trans.sa_session, job, credentials_context)
747747
self._record_inputs(trans, tool, job, incoming, inp_data, inp_dataset_collections)

lib/galaxy/tools/actions/model_operations.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ def _produce_outputs(
180180
object_store_populator = ObjectStorePopulator(trans.app, trans.user)
181181
for hdca in output_collections.out_collection_instances.values():
182182
hdca.visible = False
183-
# Would we also need to replace the datasets with skipped datasets?
184183
for data in hdca.dataset_instances:
185-
data.set_skipped(object_store_populator)
184+
data.set_skipped(object_store_populator, replace_dataset=True)
186185
trans.sa_session.add_all(out_data.values())

lib/galaxy_test/api/test_workflows.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5766,6 +5766,54 @@ def test_run_with_default_file_in_step_inline(self):
57665766
content = self.dataset_populator.get_history_dataset_content(history_id)
57675767
assert "chr1" in content
57685768

5769+
def test_conditional_skip_on_database_operation_collection_output(self):
5770+
with self.dataset_populator.test_history() as history_id:
5771+
summary = self._run_workflow(
5772+
"""
5773+
class: GalaxyWorkflow
5774+
inputs:
5775+
input_collection: collection
5776+
steps:
5777+
filter:
5778+
tool_id: __FILTER_FAILED_DATASETS__
5779+
in:
5780+
input: input_collection
5781+
when: $(false)
5782+
""",
5783+
test_data="""
5784+
input_collection:
5785+
collection_type: list
5786+
elements:
5787+
- identifier: el1
5788+
content: "test content 1"
5789+
""",
5790+
history_id=history_id,
5791+
wait=True,
5792+
assert_ok=True,
5793+
)
5794+
invocation = self.workflow_populator.get_invocation(summary.invocation_id, step_details=True)
5795+
5796+
input_hdca_details = self.dataset_populator.get_history_collection_details(
5797+
history_id, content_id=invocation["inputs"]["0"]["id"]
5798+
)
5799+
input_hda = input_hdca_details["elements"][0]["object"]
5800+
filter_content = self.dataset_populator.get_history_dataset_content(
5801+
history_id=history_id, content_id=input_hda["id"]
5802+
)
5803+
assert "test content 1" in filter_content, f"Expected 'test content 1' in input, got: {filter_content}"
5804+
5805+
# Get the filter step output
5806+
filter_step = [s for s in invocation["steps"] if s["workflow_step_label"] == "filter"][0]
5807+
filter_output_id = filter_step["output_collections"]["output"]["id"]
5808+
hdca = self.dataset_populator.get_history_collection_details(history_id, content_id=filter_output_id)
5809+
hda = hdca["elements"][0]["object"]
5810+
5811+
# Assert that the filter output dataset contains the string 'null'
5812+
filter_content = self.dataset_populator.get_history_dataset_content(
5813+
history_id=history_id, content_id=hda["id"]
5814+
)
5815+
assert "null" in filter_content, f"Expected 'null' in filter output, got: {filter_content}"
5816+
57695817
def test_conditional_flat_crossproduct_subworkflow(self):
57705818
parent = yaml.safe_load(
57715819
"""

scripts/common_startup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ if [ $SKIP_CLIENT_BUILD -eq 0 ]; then
238238
echo "Installing yarn into '$CONDA_DEFAULT_ENV' Conda environment with conda."
239239
$CONDA_EXE install --yes --override-channels --channel conda-forge --name "$CONDA_DEFAULT_ENV" 'yarn<2'
240240
elif [ -n "$VIRTUAL_ENV" ] && in_venv "$(command -v npm)"; then
241-
echo "Installing yarn into $VIRTUAL_ENV with npm."
242-
npm install --global yarn
241+
echo "Installing yarn into $VIRTUAL_ENV with corepack."
242+
corepack enable yarn
243243
else
244244
echo "Installing yarn locally with npm."
245245
npm install yarn

0 commit comments

Comments
 (0)