Skip to content

Commit 1d9d93e

Browse files
committed
Merge branch 'release_25.0' into dev
2 parents 000c2da + 14e6456 commit 1d9d93e

16 files changed

Lines changed: 272 additions & 299 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ client/webpack-stats.json
165165
packages/*/build
166166
packages/*/dist
167167
packages/*/*.egg-info
168+
packages/meta/requirements.txt
168169

169170
# Standalone script + main, or build into dist
170171
config/plugins/**/static/dist

client/src/components/Dataset/DatasetState.vue

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<script setup lang="ts">
22
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
3-
import { computed } from "vue";
3+
import { computed, ref } from "vue";
44
55
import { STATES } from "@/components/History/Content/model/states";
66
import { useDatasetStore } from "@/stores/datasetStore";
77
8+
import GTooltip from "@/components/BaseComponents/GTooltip.vue";
9+
810
const datasetStore = useDatasetStore();
911
1012
const props = defineProps<{
@@ -24,13 +26,28 @@ const contentCls = computed(() => {
2426
return `alert-${status}`;
2527
}
2628
});
29+
30+
const displayText = computed(() => {
31+
if (contentState.value?.displayName) {
32+
return contentState.value.displayName;
33+
}
34+
35+
const state = dataset.value?.state;
36+
return state ? state.replace(/_/g, " ") : "n/a";
37+
});
38+
39+
const tooltipText = computed(() => contentState.value?.text || null);
40+
41+
const stateBadgeRef = ref<HTMLElement | null>(null);
2742
</script>
2843

2944
<template>
30-
<span v-if="dataset && contentState" class="rounded px-2 py-1 ml-2" :class="contentCls">
45+
<span v-if="dataset && contentState" ref="stateBadgeRef" class="rounded px-2 py-1 ml-2" :class="contentCls">
3146
<span v-if="contentState.icon" class="mr-1">
3247
<FontAwesomeIcon fixed-width :icon="contentState.icon" :spin="contentState.spin" />
3348
</span>
34-
{{ contentState.text || dataset.state || "n/a" }}
49+
{{ displayText }}
50+
51+
<GTooltip v-if="tooltipText" :reference="stateBadgeRef" :text="tooltipText" />
3552
</span>
3653
</template>

client/src/components/Dataset/DatasetView.vue

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ watch(
102102
class="flex-grow-1"
103103
:collapse="headerState"
104104
@click="toggleHeaderCollapse">
105-
<span class="dataset-hid">{{ dataset?.hid }}:</span>
106-
<span class="dataset-name font-weight-bold">{{ dataset?.name }}</span>
107-
<span class="dataset-state-header">
108-
<DatasetState :dataset-id="datasetId" />
109-
</span>
105+
<div class="dataset-header-content">
106+
<div class="dataset-title-row">
107+
<span class="dataset-hid">{{ dataset?.hid }}:</span>
108+
<span class="dataset-name font-weight-bold">{{ dataset?.name }}</span>
109+
</div>
110+
<span class="dataset-state-header">
111+
<DatasetState :dataset-id="datasetId" />
112+
</span>
113+
</div>
110114
</Heading>
111115
</div>
112116
<transition v-if="dataset" name="header">
@@ -279,23 +283,34 @@ watch(
279283
opacity: 0;
280284
}
281285
282-
.dataset-hid,
283-
.dataset-state-header {
284-
white-space: nowrap;
286+
.dataset-header-content {
287+
display: flex;
288+
flex-wrap: wrap;
289+
align-items: baseline;
290+
gap: 0.5rem;
291+
}
292+
293+
.dataset-title-row {
294+
display: flex;
295+
align-items: baseline;
296+
min-width: 0;
297+
flex: 1 1 auto;
285298
}
286299
287300
.dataset-hid {
301+
white-space: nowrap;
288302
margin-right: 0.25rem;
289303
}
290304
291305
.dataset-name {
292306
word-break: break-word;
307+
min-width: 0;
293308
}
294309
295310
.dataset-state-header {
296311
font-size: $h5-font-size;
297-
vertical-align: middle;
298-
margin-left: 0.5rem;
312+
flex: 0 0 auto;
313+
white-space: nowrap;
299314
}
300315
301316
.tab-content-panel {

client/src/components/History/Content/model/states.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type State =
1414
interface StateRepresentation {
1515
status: "success" | "warning" | "info" | "danger" | "secondary";
1616
text?: string;
17+
displayName?: string;
1718
icon?: string;
1819
spin?: boolean;
1920
nonDb?: boolean;
@@ -31,84 +32,98 @@ export const STATES: StateMap = {
3132
/** has successfully completed running */
3233
ok: {
3334
status: "success",
35+
displayName: "ok",
3436
},
3537
/** has no data */
3638
empty: {
3739
status: "success",
3840
text: "No data.",
41+
displayName: "empty",
3942
},
4043
/** was created without a tool */
4144
new: {
4245
status: "warning",
4346
text: "This is a new dataset and not all of its data are available yet.",
47+
displayName: "new",
4448
icon: "clock",
4549
},
4650
/** the job that will produce the dataset queued in the runner */
4751
queued: {
4852
status: "warning",
4953
text: "This job is waiting to run.",
54+
displayName: "queued",
5055
icon: "clock",
5156
},
5257
/** the job that will produce the dataset is running */
5358
running: {
5459
status: "warning",
5560
text: "This job is currently running.",
61+
displayName: "running",
5662
icon: "spinner",
5763
spin: true,
5864
},
5965
/** metadata for the dataset is being discovered/set */
6066
setting_metadata: {
6167
status: "warning",
6268
text: "Metadata is being auto-detected.",
69+
displayName: "setting metadata",
6370
icon: "spinner",
6471
spin: true,
6572
},
6673
/** is uploading and not ready */
6774
upload: {
6875
status: "warning",
6976
text: "This dataset is currently uploading.",
77+
displayName: "uploading",
7078
icon: "spinner",
7179
spin: true,
7280
},
7381
/** remote dataset */
7482
deferred: {
7583
status: "info",
7684
text: "This dataset is remote, has not been ingested by Galaxy, and full metadata may not be available.",
85+
displayName: "deferred",
7786
icon: "cloud",
7887
},
7988
/** the job that will produce the dataset paused */
8089
paused: {
8190
status: "info",
8291
text: "This job is paused. Use the 'Resume Paused Jobs' in the history menu to resume.",
92+
displayName: "paused",
8393
icon: "pause",
8494
},
8595
/** deleted while uploading */
8696
discarded: {
8797
status: "danger",
8898
text: "This dataset is discarded - the job creating it may have been cancelled or it may have been imported without file data.",
99+
displayName: "discarded",
89100
icon: "exclamation-triangle",
90101
},
91102
/** the tool producing this dataset has errored */
92103
error: {
93104
status: "danger",
94105
text: "An error occurred with this dataset.",
106+
displayName: "error",
95107
icon: "exclamation-triangle",
96108
},
97109
/** metadata discovery/setting failed or errored (but otherwise ok) */
98110
failed_metadata: {
99111
status: "danger",
100112
text: "Metadata generation failed. Please retry.",
113+
displayName: "failed metadata",
101114
icon: "exclamation-triangle",
102115
},
103116
/** the job has failed, this is not a dataset but a job state used in the collection job state summary. */
104117
failed: {
105118
status: "danger",
119+
displayName: "failed",
106120
icon: "exclamation-triangle",
107121
},
108122
/** the dataset is not yet loaded in the UI. This state is only visual and transitional, it does not exist in the database. */
109123
placeholder: {
110124
status: "secondary",
111125
text: "This dataset is being fetched.",
126+
displayName: "loading",
112127
icon: "spinner",
113128
spin: true,
114129
nonDb: true,
@@ -117,19 +132,22 @@ export const STATES: StateMap = {
117132
failed_populated_state: {
118133
status: "danger",
119134
text: "Failed to populate the collection.",
135+
displayName: "failed",
120136
icon: "exclamation-triangle",
121137
nonDb: true,
122138
},
123139
/** the `populated_state: new`. This state is only visual and transitional, it does not exist in the database. */
124140
new_populated_state: {
125141
status: "warning",
126142
text: "This is a new collection and not all of its data are available yet.",
143+
displayName: "new",
127144
icon: "clock",
128145
nonDb: true,
129146
},
130147
inaccessible: {
131148
status: "warning",
132149
text: "User not allowed to access this dataset.",
150+
displayName: "inaccessible",
133151
icon: "lock",
134152
nonDb: true,
135153
},

lib/galaxy/job_execution/datasets.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515
HistoryDatasetCollectionAssociation,
1616
)
1717

18-
DeferrableObjectsT = Union[DatasetInstance, HistoryDatasetCollectionAssociation, DatasetCollectionElement]
18+
DeferrableObjectsT = Union[
19+
DatasetInstance,
20+
HistoryDatasetCollectionAssociation,
21+
DatasetCollectionElement,
22+
list[DatasetInstance],
23+
list[Union[HistoryDatasetCollectionAssociation, DatasetCollectionElement]],
24+
list[Union[DatasetInstance, HistoryDatasetCollectionAssociation, DatasetCollectionElement]],
25+
]
1926

2027

2128
def dataset_path_rewrites(dataset_paths):

lib/galaxy/model/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9003,6 +9003,13 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl
90039003
states = InvocationState
90049004
non_terminal_states = [states.NEW, states.READY]
90059005

9006+
def get_last_workflow_invocation_step_update_time(self) -> Optional[datetime]:
9007+
session = required_object_session(self)
9008+
stmt = select(func.max(WorkflowInvocationStep.update_time)).where(
9009+
WorkflowInvocationStep.workflow_invocation_id == self.id
9010+
)
9011+
return session.execute(stmt).scalar_one_or_none()
9012+
90069013
def create_subworkflow_invocation_for_step(self, step):
90079014
assert step.type == "subworkflow"
90089015
subworkflow_invocation = WorkflowInvocation()

lib/galaxy/model/deferred.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def __init__(
8989
self._object_store_populator = object_store_populator
9090
self._file_sources = file_sources
9191
self._sa_session = sa_session
92+
self._previously_materialized: dict[int, HistoryDatasetAssociation] = {}
9293

9394
def ensure_materialized(
9495
self,
@@ -106,6 +107,12 @@ def ensure_materialized(
106107
if dataset.state != Dataset.states.DEFERRED and isinstance(dataset_instance, HistoryDatasetAssociation):
107108
return dataset_instance
108109

110+
if dataset_instance.id in self._previously_materialized and isinstance(
111+
dataset_instance, HistoryDatasetAssociation
112+
):
113+
# If we have already materialized this dataset, return the previously materialized instance.
114+
return self._previously_materialized[dataset_instance.id]
115+
109116
materialized_dataset_hashes = [h.copy() for h in dataset.hashes]
110117
if in_place:
111118
materialized_dataset = dataset_instance.dataset
@@ -211,6 +218,7 @@ def ensure_materialized(
211218
metadata_tmp_files_dir = None
212219
materialized_dataset_instance.set_meta(metadata_tmp_files_dir=metadata_tmp_files_dir)
213220
materialized_dataset_instance.metadata_deferred = False
221+
self._previously_materialized[dataset_instance.id] = materialized_dataset_instance
214222
return materialized_dataset_instance
215223

216224
def _stream_source(self, target_source: DatasetSource, datatype, dataset: Dataset) -> str:

lib/galaxy/tool_util_models/parameters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,14 +921,14 @@ def validate_color_str_or_connected_value(value) -> str:
921921

922922
def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation:
923923
py_type = self.py_type
924-
initialize: Any = ...
924+
requires_value = self.request_requires_value
925+
initialize = ... if requires_value else None
925926
if state_representation == "workflow_step_linked":
926927
py_type = allow_connected_value(py_type)
927928
validators = {
928929
"color_format": field_validator(self.name)(ColorParameterModel.validate_color_str_or_connected_value)
929930
}
930931
elif state_representation == "workflow_step":
931-
initialize = None
932932
validators = {"color_format": field_validator(self.name)(ColorParameterModel.validate_color_str_if_value)}
933933
else:
934934
validators = {"color_format": field_validator(self.name)(ColorParameterModel.validate_color_str)}

0 commit comments

Comments
 (0)