Skip to content

Commit f823afc

Browse files
Merge remote-tracking branch 'upstream/release_25.0' into merge_25.0_into_dev_sept
2 parents 6685248 + 89b9a50 commit f823afc

18 files changed

Lines changed: 209 additions & 80 deletions

File tree

.github/workflows/integration.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
uses: medyagh/setup-minikube@latest
6565
with:
6666
driver: none
67-
kubernetes-version: '1.23.0'
67+
kubernetes-version: '1.28.0'
6868
- name: Check pods
6969
run: kubectl get pods -A
7070
- uses: actions/checkout@v4

client/src/components/History/Content/GenericItem.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<template>
2-
<component :is="providerComponent" :id="itemId" v-slot="{ result: item, loading }" auto-refresh>
2+
<component
3+
:is="providerComponent"
4+
:id="itemId"
5+
:key="view"
6+
v-slot="{ result: item, loading }"
7+
:view="view"
8+
auto-refresh>
39
<LoadingSpan v-if="loading" message="Loading dataset" />
410
<div v-else>
511
<ContentItem
@@ -11,7 +17,7 @@
1117
:expand-dataset="expandDataset"
1218
:is-dataset="item.history_content_type == 'dataset' || item.element_type == 'hda'"
1319
@update:expand-dataset="expandDataset = $event"
14-
@view-collection="viewCollection = !viewCollection"
20+
@view-collection="onViewCollection"
1521
@delete="onDelete"
1622
@toggleHighlights="onHighlight(item)"
1723
@undelete="onUndelete(item)"
@@ -60,6 +66,7 @@ export default {
6066
return {
6167
viewCollection: false,
6268
expandDataset: false,
69+
view: this.itemSrc === "hdca" ? "collection" : "element",
6370
};
6471
},
6572
computed: {
@@ -124,6 +131,12 @@ export default {
124131
this.onError(error, "Failed to highlight related items");
125132
}
126133
},
134+
onViewCollection(collection) {
135+
if (this.view === "collection" && collection.model_class === "HistoryDatasetCollectionAssociation") {
136+
this.view = "element";
137+
}
138+
this.viewCollection = !this.viewCollection;
139+
},
127140
},
128141
};
129142
</script>

client/src/components/Tool/ToolForm.vue

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
id="execute"
8888
class="text-nowrap"
8989
title="Run Tool"
90-
:disabled="!canMutateHistory"
90+
:disabled="runButtonDisabled"
9191
size="small"
9292
:wait="showExecuting"
9393
:tooltip="tooltip"
@@ -97,7 +97,7 @@
9797
<ButtonSpinner
9898
title="Run Tool"
9999
class="mt-3 mb-3"
100-
:disabled="!canMutateHistory"
100+
:disabled="runButtonDisabled"
101101
:wait="showExecuting"
102102
:tooltip="tooltip"
103103
@onClick="onExecute(config, currentHistoryId)" />
@@ -205,6 +205,7 @@ export default {
205205
],
206206
immutableHistoryMessage:
207207
"This history is immutable and you cannot run tools in it. Please switch to a different history.",
208+
formConfigInitialized: false,
208209
};
209210
},
210211
computed: {
@@ -227,6 +228,12 @@ export default {
227228
if (!this.canMutateHistory) {
228229
return this.immutableHistoryMessage;
229230
}
231+
if (this.hasConfigOrValErrors) {
232+
return "Please resolve highlighted issues before running the tool.";
233+
}
234+
if (this.showExecuting) {
235+
return "Tool is being executed...";
236+
}
230237
return `Run tool: ${this.formConfig.name} (${this.formConfig.version})`;
231238
},
232239
errorContentPretty() {
@@ -252,8 +259,15 @@ export default {
252259
canMutateHistory() {
253260
return this.currentHistory && canMutateHistory(this.currentHistory);
254261
},
255-
runButtonTitle() {
256-
return "Run Tool";
262+
runButtonDisabled() {
263+
return this.disabled || !this.canMutateHistory || this.hasConfigOrValErrors;
264+
},
265+
/** If there are any backend returned `formConfig.errors` or internal/client checked validation errors. */
266+
hasConfigOrValErrors() {
267+
return (
268+
(this.formConfig.errors && Object.values(this.formConfig.errors).length > 0) ||
269+
this.validationInternal?.length
270+
);
257271
},
258272
},
259273
watch: {
@@ -286,7 +300,12 @@ export default {
286300
this.formData = newData;
287301
if (refreshRequest) {
288302
this.onUpdate();
303+
} else if (this.formConfigInitialized && this.hasConfigOrValErrors) {
304+
// After the first manual change to a form input, for every change, if there isn't a request to refresh,
305+
// we reset the errors since we haven't received a tool form update via the backend.
306+
this.formConfig.errors = null;
289307
}
308+
this.formConfigInitialized = true;
290309
},
291310
onUpdate() {
292311
this.disabled = true;
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { SingleQueryProvider } from "components/providers/SingleQueryProvider";
22

3-
import { fetchCollectionDetails } from "@/api/datasetCollections";
3+
import { fetchCollectionDetails, fetchCollectionSummary } from "@/api/datasetCollections";
44

55
// There isn't really a good way to know when to stop polling for HDCA updates,
66
// but we know the populated_state should at least be ok.
77
export default SingleQueryProvider(
8-
(params) => fetchCollectionDetails({ hdca_id: params.id }),
8+
(params) => {
9+
if (params.view && params.view === "collection") {
10+
return fetchCollectionSummary({ hdca_id: params.id });
11+
}
12+
return fetchCollectionDetails({ hdca_id: params.id });
13+
},
914
(result) => result.populated_state === "ok",
1015
);

client/src/composables/useInvocationGraph.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "@fortawesome/free-solid-svg-icons";
1111
import { computed, type Ref, ref, set } from "vue";
1212

13-
import { fetchCollectionDetails } from "@/api/datasetCollections";
13+
import { fetchCollectionSummary } from "@/api/datasetCollections";
1414
import { fetchDatasetDetails } from "@/api/datasets";
1515
import type { InvocationStep, StepJobSummary, WorkflowInvocationElementView } from "@/api/invocations";
1616
import type { StoredWorkflowDetailed } from "@/api/workflows";
@@ -323,7 +323,7 @@ export function useInvocationGraph(
323323
set(graphStep, "state", getContentItemState(hda));
324324
set(graphStep, "nodeText", `${hda.hid}: <b>${hda.name}</b>`);
325325
} else {
326-
const hdca = await fetchCollectionDetails({ hdca_id: inputItem.id });
326+
const hdca = await fetchCollectionSummary({ hdca_id: inputItem.id });
327327
// TODO: Same type mismatch as above
328328
set(graphStep, "state", getContentItemState(hdca));
329329
set(graphStep, "nodeText", `${hdca.hid}: <b>${hdca.name}</b>`);

doc/source/admin/production.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,5 @@ The list of tasks that are currently handled by `Celery` can be found in `lib/ga
194194
To enable Celery in your instance you need to follow some additional steps:
195195

196196
- Set `enable_celery_tasks: true` in the Galaxy config.
197-
- Configure the `backend` under `celery_conf` to store the results of the tasks. For example, you can use [`redis` as the backend](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html#broker-redis). If you are using `redis`, make sure to install the `redis` dependency in your Galaxy environment with `pip install redis`. You can find more information on how to configure other backends in the [Celery documentation](https://docs.celeryq.dev/en/stable/userguide/tasks.html#task-result-backends).
197+
- Configure the `backend` under `celery_conf` to store the results of the tasks. For example, you can use [`redis` as the backend](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html#broker-redis). If you are using `redis`, make sure to install the `redis` dependency in your Galaxy environment with `pip install redis`. You can find more information on how to configure other backends in the [Celery documentation](https://docs.celeryq.dev/en/stable/userguide/tasks.html#task-result-backends). Keep in mind that you should not reuse the main Galaxy database as a backend for Celery.
198198
- Configure one or more workers to handle the tasks. You can find more information on how to configure workers in the [Celery documentation](https://docs.celeryq.dev/en/stable/userguide/workers.html). If you are using [Gravity](https://github.com/galaxyproject/gravity) it will simplify the process of setting up Celery workers.

lib/galaxy/authnz/custos_authnz.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import jwt
1818
from oauthlib.common import generate_nonce
1919
from requests_oauthlib import OAuth2Session
20+
from sqlalchemy import func
2021

2122
from galaxy import (
2223
exceptions,
@@ -121,11 +122,19 @@ def refresh(self, trans, custos_authnz_token):
121122
return False
122123
if not custos_authnz_token.refresh_token:
123124
return False
124-
refresh_token_decoded = self._decode_token_no_signature(custos_authnz_token.refresh_token)
125-
# do not attempt to use refresh token that is already expired
126-
if int(refresh_token_decoded["exp"]) <= int(time.time()):
127-
# in the future we might want to log out the user here
125+
126+
# Try to extract expiration date from the refresh token. If expired, do not refresh token.
127+
try:
128+
refresh_token_decoded = self._decode_token_no_signature(custos_authnz_token.refresh_token)
129+
# do not attempt to use refresh token that is already expired
130+
if int(refresh_token_decoded["exp"]) <= int(time.time()):
131+
# in the future we might want to log out the user here
132+
return False
133+
except jwt.exceptions.DecodeError:
134+
log.warning("Refresh token cannot be decoded. Galaxy does not support non-decodable refresh tokens.")
135+
# If the refresh token is non-decodable, we do not use it because we cannot reliably determine its expiration date. See discussion in https://github.com/galaxyproject/galaxy/pull/20821
128136
return False
137+
129138
oauth2_session = self._create_oauth2_session()
130139
token_endpoint = self.config.token_endpoint
131140
if self.config.iam_client_secret:
@@ -237,7 +246,7 @@ def callback(self, state_token, authz_code, trans, login_redirect_url):
237246
custos_authnz_token = self._get_custos_authnz_token(trans.sa_session, user_id, self.config.provider)
238247
if custos_authnz_token is None:
239248
user = trans.user
240-
existing_user = trans.sa_session.query(User).filter_by(email=email).first()
249+
existing_user = trans.sa_session.query(User).where(func.lower(User.email) == email.lower()).first()
241250
if not user:
242251
if existing_user:
243252
if trans.app.config.fixed_delegated_auth:

lib/galaxy/config/sample/datatypes_conf.xml.sample

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,12 @@
234234
<datatype extension="h5ad" type="galaxy.datatypes.binary:Anndata" description="An HDF5-based anndata File" mimetype="application/octet-stream" display_in_upload="true"/>
235235
<datatype extension="h5mu" type="galaxy.datatypes.binary:H5" mimetype="application/octet-stream" display_in_upload="true" subclass="true" description="MuData is a format analogous to Anndata for multimodal datasets and is based on HDF5" description_url="https://github.com/scverse/mudata"/>
236236
<datatype extension="visium.tar.gz" type="galaxy.datatypes.binary:Visium" subclass="true" display_in_upload="true" description="Visium is a tar.gz archive with at least a 'Spatial' subfolder, a filtered h5 file and a raw h5 file." />
237-
<datatype extension="mz5" type="galaxy.datatypes.binary:H5" subclass="true" mimetype="application/octet-stream" display_in_upload="true" description="Mz5 is an HDF5-based open mass spectrometry file format modeled after mzML." />
237+
<datatype extension="mz5" type="galaxy.datatypes.binary:H5" subclass="true" mimetype="application/octet-stream" display_in_upload="true" description="Mz5 is an HDF5-based open mass spectrometry file format modeled after mzML." />
238238
<datatype extension="mzmlb" type="galaxy.datatypes.binary:H5" subclass="true" mimetype="application/octet-stream" display_in_upload="true" description="MzMLb is an HDF5-based open mass spectrometry file format that stores metadata as mzML and binary data as native HDF5 types." />
239239
<datatype extension="mbi" type="galaxy.datatypes.binary:H5" subclass="true" mimetype="application/octet-stream" display_in_upload="true" description="MBI is MOBILion's proprietary HDF5-based format for its ion mobility mass spectrometry data." />
240240
<datatype extension="hyphy_results.json" type="galaxy.datatypes.text:Json" mimetype="application/json" subclass="true" display_in_upload="false"/>
241241
<datatype extension="hivtrace" type="galaxy.datatypes.text:Json" mimetype="application/json" subclass="true" display_in_upload="false"/>
242+
<datatype extension="hic" type="galaxy.datatypes.binary:Hic" mimetype="application/octet-stream" display_in_upload="true"/>
242243
<datatype extension="cool" type="galaxy.datatypes.binary:Cool" mimetype="application/octet-stream" display_in_upload="true"/>
243244
<datatype extension="mcool" type="galaxy.datatypes.binary:MCool" mimetype="application/octet-stream" display_in_upload="true"/>
244245
<datatype extension="h5mlm" type="galaxy.datatypes.binary:H5MLM" mimetype="application/octet-stream" display_in_upload="true"/>
@@ -698,6 +699,9 @@
698699
<datatype extension="docx" type="galaxy.datatypes.binary:Docx" display_in_upload="true" decription="DOCX is an XML-based file format that is natively used for Microsoft Word documents" description_url="https://www.iso.org/standard/71691.html">
699700
<infer_from suffix="docx" />
700701
</datatype>
702+
<datatype extension="pptx" type="galaxy.datatypes.binary:Pptx" display_in_upload="true" decription="PPTX is an XML-based file format that is natively used for Microsoft PowerPoint presentations" description_url="https://www.iso.org/standard/71691.html">
703+
<infer_from suffix="pptx" />
704+
</datatype>
701705
<datatype extension="btwisted" type="galaxy.datatypes.data:Text" subclass="true"/>
702706
<datatype extension="cai" type="galaxy.datatypes.data:Text" subclass="true"/>
703707
<datatype extension="cat_db" type="galaxy.datatypes.data:Text" subclass="true"/>
@@ -1228,6 +1232,7 @@
12281232
<sniffer type="galaxy.datatypes.binary:NcbiTaxonomySQlite"/>
12291233
<sniffer type="galaxy.datatypes.binary:SQlite"/>
12301234
<sniffer type="galaxy.datatypes.binary:H5MLM"/>
1235+
<sniffer type="galaxy.datatypes.binary:Hic"/>
12311236
<sniffer type="galaxy.datatypes.binary:Cool"/>
12321237
<sniffer type="galaxy.datatypes.binary:MCool"/>
12331238
<sniffer type="galaxy.datatypes.binary:Loom"/>
@@ -1273,6 +1278,8 @@
12731278
<sniffer type="galaxy.datatypes.binary:Edr"/>
12741279
<sniffer type="galaxy.datatypes.binary:Vel"/>
12751280
<sniffer type="galaxy.datatypes.binary:Xlsx"/>
1281+
<sniffer type="galaxy.datatypes.binary:Docx"/>
1282+
<sniffer type="galaxy.datatypes.binary:Pptx"/>
12761283
<sniffer type="galaxy.datatypes.binary:Numpy"/>
12771284
<sniffer type="galaxy.datatypes.qiime2:QIIME2Metadata"/>
12781285
<sniffer type="galaxy.datatypes.qiime2:QIIME2Artifact"/>

lib/galaxy/datatypes/binary.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,6 +3192,7 @@ class Docx(Binary):
31923192

31933193
file_ext = "docx"
31943194
compressed = True
3195+
display_behavior = "download" # Office documents trigger downloads
31953196

31963197
def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
31973198
# Docx is compressed in zip format and must not be uncompressed in Galaxy.
@@ -3214,6 +3215,22 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
32143215
return file_prefix.compressed_mime_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
32153216

32163217

3218+
@build_sniff_from_prefix
3219+
class Pptx(Binary):
3220+
"""Class for PowerPoint 2007 (pptx) files"""
3221+
3222+
file_ext = "pptx"
3223+
compressed = True
3224+
display_behavior = "download" # Office documents trigger downloads
3225+
3226+
def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
3227+
# Pptx is compressed in zip format and must not be uncompressed in Galaxy.
3228+
return (
3229+
file_prefix.compressed_mime_type
3230+
== "application/vnd.openxmlformats-officedocument.presentationml.presentation"
3231+
)
3232+
3233+
32173234
@build_sniff_from_prefix
32183235
class ExcelXls(Binary):
32193236
"""Class describing an Excel (xls) file"""
@@ -4778,3 +4795,63 @@ def display_peek(self, dataset: DatasetProtocol) -> str:
47784795
return dataset.peek
47794796
except Exception:
47804797
return f"Binary numpy file ({nice_size(dataset.get_size())})"
4798+
4799+
4800+
@build_sniff_from_prefix
4801+
class Hic(Binary):
4802+
"""
4803+
Hic: highly compressed binary file that stores contact matrices
4804+
from multiple resolutions in a clever way, allowing random access.
4805+
https://github.com/aidenlab/hic-format
4806+
4807+
>>> from galaxy.datatypes.sniff import get_test_fname
4808+
>>> fname = get_test_fname('merlin.hic')
4809+
>>> Hic().sniff(fname)
4810+
True
4811+
>>> fname = get_test_fname('test.mz5')
4812+
>>> Hic().sniff(fname)
4813+
False
4814+
"""
4815+
4816+
file_ext = "hic"
4817+
4818+
MetadataElement(
4819+
name="version",
4820+
default="",
4821+
param=MetadataParameter,
4822+
desc="Version of the HiC file format",
4823+
readonly=True,
4824+
visible=True,
4825+
no_value=0,
4826+
optional=True,
4827+
)
4828+
4829+
def __init__(self, **kwd):
4830+
super().__init__(**kwd)
4831+
self._magic = b"HIC"
4832+
4833+
def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
4834+
return file_prefix.startswith_bytes(self._magic)
4835+
4836+
def set_peek(self, dataset: DatasetProtocol, **kwd) -> None:
4837+
if not dataset.dataset.purged:
4838+
dataset.peek = "Binary HiC file"
4839+
dataset.blurb = f"{nice_size(dataset.get_size())}"
4840+
dataset.blurb += f"\nHiC Format v{dataset.metadata.version}"
4841+
else:
4842+
dataset.peek = "file does not exist"
4843+
dataset.blurb = "file purged from disk"
4844+
4845+
def display_peek(self, dataset: DatasetProtocol) -> str:
4846+
try:
4847+
return dataset.peek
4848+
except Exception:
4849+
return f"Binary HiC file ({nice_size(dataset.get_size())})"
4850+
4851+
def set_meta(self, dataset: DatasetProtocol, overwrite: bool = True, **kwd) -> None:
4852+
"""
4853+
Set metadata for HiC file.
4854+
"""
4855+
with open(dataset.get_file_name(), "rb") as handle:
4856+
header_bytes = handle.read(8)
4857+
dataset.metadata.version = struct.unpack("<i", header_bytes[4:8])[0]

lib/galaxy/datatypes/sniff.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@
5151
log = logging.getLogger(__name__)
5252

5353
SNIFF_PREFIX_BYTES = int(os.environ.get("GALAXY_SNIFF_PREFIX_BYTES", None) or 2**20)
54-
BINARY_MIMETYPES = {"application/pdf", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}
54+
BINARY_MIMETYPES = {
55+
"application/pdf",
56+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
57+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
58+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
59+
}
5560

5661

5762
def get_test_fname(fname):

0 commit comments

Comments
 (0)