Skip to content

Commit 01209e1

Browse files
authored
Merge pull request #20439 from dannon/preferred-viz-updates
[25.0] Dataset Display and Preferred Viz fixes
2 parents 29b97b0 + 8bd9306 commit 01209e1

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed

lib/galaxy/datatypes/images.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import numpy as np
2121
import png
2222
import tifffile
23+
from typing_extensions import Literal
2324

2425
try:
2526
import PIL
@@ -65,6 +66,7 @@ class Image(data.Data):
6566
edam_data = "data_2968"
6667
edam_format = "format_3547"
6768
file_ext = ""
69+
display_behavior: Literal["inline", "download"] = "inline" # Most image formats can be displayed inline in browsers
6870

6971
MetadataElement(
7072
name="axes",
@@ -419,18 +421,22 @@ def set_peek(self, dataset: DatasetProtocol, **kwd) -> None:
419421

420422
class Hamamatsu(Image):
421423
file_ext = "vms"
424+
display_behavior = "download" # Proprietary microscopy format, not browser-displayable
422425

423426

424427
class Mirax(Image):
425428
file_ext = "mrxs"
429+
display_behavior = "download" # Proprietary microscopy format, not browser-displayable
426430

427431

428432
class Sakura(Image):
429433
file_ext = "svslide"
434+
display_behavior = "download" # Proprietary microscopy format, not browser-displayable
430435

431436

432437
class Nrrd(Image):
433438
file_ext = "nrrd"
439+
display_behavior = "download" # Medical imaging format, not browser-displayable
434440

435441

436442
class Bmp(Image):

lib/galaxy/datatypes/registry.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -516,22 +516,6 @@ def get_legacy_sites_by_build(self, site_type, build):
516516
def get_display_sites(self, site_type):
517517
return self.display_sites.get(site_type, [])
518518

519-
def get_preferred_visualization(self, datatype_extension):
520-
"""
521-
Get the preferred visualization mapping for a specific datatype extension.
522-
Returns a dictionary with 'visualization' and 'default_params' keys, or None if no mapping exists.
523-
524-
Preferred visualizations are defined inline within each datatype definition in the
525-
datatypes_conf.xml configuration file. These mappings determine which visualization plugin
526-
should be used by default when viewing datasets of a specific type.
527-
528-
Example configuration:
529-
<datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true">
530-
<visualization plugin="igv" />
531-
</datatype>
532-
"""
533-
return self.visualization_mappings.get(datatype_extension)
534-
535519
def get_all_visualization_mappings(self):
536520
"""
537521
Get all datatype to visualization mappings.

lib/galaxy/managers/datatypes.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,51 @@ def view_visualization_mappings(
163163
return parse_obj_as(DatatypeVisualizationMappingsList, mappings)
164164

165165

166+
def get_preferred_visualization(datatypes_registry: Registry, datatype_extension: str) -> Optional[Dict[str, str]]:
167+
"""
168+
Get the preferred visualization mapping for a specific datatype extension.
169+
Returns a dictionary with 'visualization' and 'default_params' keys, or None if no mapping exists.
170+
171+
Preferred visualizations are defined inline within each datatype definition in the
172+
datatypes_conf.xml configuration file. These mappings determine which visualization plugin
173+
should be used by default when viewing datasets of a specific type.
174+
175+
If no direct mapping exists for the extension, this method will walk up the inheritance
176+
chain to find a preferred visualization from a parent datatype class.
177+
178+
Example configuration:
179+
<datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true">
180+
<visualization plugin="igv" />
181+
</datatype>
182+
"""
183+
direct_mapping = datatypes_registry.visualization_mappings.get(datatype_extension)
184+
if direct_mapping:
185+
return direct_mapping
186+
187+
current_datatype = datatypes_registry.get_datatype_by_extension(datatype_extension)
188+
if not current_datatype:
189+
return None
190+
191+
# Use the same mapping approach as the datatypes API for consistency
192+
mapping_data = view_mapping(datatypes_registry)
193+
194+
current_class_name = mapping_data.ext_to_class_name.get(datatype_extension)
195+
if not current_class_name:
196+
return None
197+
198+
current_class_mappings = mapping_data.class_to_classes.get(current_class_name, {})
199+
200+
for ext, visualization_mapping in datatypes_registry.visualization_mappings.items():
201+
if ext == datatype_extension:
202+
continue
203+
204+
parent_class_name = mapping_data.ext_to_class_name.get(ext)
205+
if parent_class_name and parent_class_name in current_class_mappings:
206+
return visualization_mapping
207+
208+
return None
209+
210+
166211
__all__ = (
167212
"DatatypeConverterList",
168213
"DatatypeDetails",
@@ -179,4 +224,5 @@ def view_visualization_mappings(
179224
"view_edam_formats",
180225
"view_edam_data",
181226
"view_visualization_mappings",
227+
"get_preferred_visualization",
182228
)

lib/galaxy/webapps/galaxy/api/datatypes.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
)
1919

2020
from galaxy.datatypes.registry import Registry
21+
from galaxy.exceptions import ObjectNotFound
2122
from galaxy.managers.datatypes import (
2223
DatatypeConverterList,
2324
DatatypeDetails,
2425
DatatypesCombinedMap,
2526
DatatypesEDAMDetailsDict,
2627
DatatypesMap,
2728
DatatypeVisualizationMappingsList,
29+
get_preferred_visualization,
2830
view_converters,
2931
view_edam_data,
3032
view_edam_formats,
@@ -33,8 +35,10 @@
3335
view_sniffers,
3436
view_visualization_mappings,
3537
)
38+
from galaxy.structured_app import StructuredApp
3639
from . import (
3740
depends,
41+
DependsOnApp,
3842
Router,
3943
)
4044

@@ -64,6 +68,7 @@
6468
@router.cbv
6569
class FastAPIDatatypes:
6670
datatypes_registry: Registry = depends(Registry)
71+
app: StructuredApp = DependsOnApp
6772

6873
@router.get(
6974
"/api/datatypes",
@@ -253,11 +258,24 @@ async def show(
253258
if converters:
254259
result["converters"] = list(converters.keys())
255260

256-
# Add preferred visualization if any
257-
preferred_viz = self.datatypes_registry.get_preferred_visualization(datatype)
261+
# Add preferred visualization if any and if the plugin is available
262+
preferred_viz = get_preferred_visualization(self.datatypes_registry, datatype)
258263
if preferred_viz:
259-
result["preferred_visualization"] = {
260-
"visualization": preferred_viz["visualization"],
261-
}
264+
plugin_name = preferred_viz["visualization"]
265+
266+
# Check if the visualization plugin is actually available
267+
try:
268+
if self.app.visualizations_registry:
269+
self.app.visualizations_registry.get_plugin(plugin_name)
270+
result["preferred_visualization"] = {
271+
"visualization": plugin_name,
272+
}
273+
else:
274+
log.warning(
275+
f"Visualizations registry not available, skipping preferred visualization for '{datatype}'"
276+
)
277+
except ObjectNotFound:
278+
# Plugin not available, don't include preferred_visualization
279+
log.warning(f"Preferred visualization '{plugin_name}' for datatype '{datatype}' is not available")
262280

263281
return result

0 commit comments

Comments
 (0)