Unverified Commit 01209e17 authored by Marius van den Beek's avatar Marius van den Beek Committed by GitHub
Browse files

Merge pull request #20439 from dannon/preferred-viz-updates

[25.0] Dataset Display and Preferred Viz fixes
parents 29b97b0a 8bd93063
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import mrcfile
import numpy as np
import png
import tifffile
from typing_extensions import Literal

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

    MetadataElement(
        name="axes",
@@ -419,18 +421,22 @@ class OMEZarr(data.ZarrDirectory):

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


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


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


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


class Bmp(Image):
+0 −16
Original line number Diff line number Diff line
@@ -516,22 +516,6 @@ class Registry:
    def get_display_sites(self, site_type):
        return self.display_sites.get(site_type, [])

    def get_preferred_visualization(self, datatype_extension):
        """
        Get the preferred visualization mapping for a specific datatype extension.
        Returns a dictionary with 'visualization' and 'default_params' keys, or None if no mapping exists.

        Preferred visualizations are defined inline within each datatype definition in the
        datatypes_conf.xml configuration file. These mappings determine which visualization plugin
        should be used by default when viewing datasets of a specific type.

        Example configuration:
        <datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true">
            <visualization plugin="igv" />
        </datatype>
        """
        return self.visualization_mappings.get(datatype_extension)

    def get_all_visualization_mappings(self):
        """
        Get all datatype to visualization mappings.
+46 −0
Original line number Diff line number Diff line
@@ -163,6 +163,51 @@ def view_visualization_mappings(
    return parse_obj_as(DatatypeVisualizationMappingsList, mappings)


def get_preferred_visualization(datatypes_registry: Registry, datatype_extension: str) -> Optional[Dict[str, str]]:
    """
    Get the preferred visualization mapping for a specific datatype extension.
    Returns a dictionary with 'visualization' and 'default_params' keys, or None if no mapping exists.

    Preferred visualizations are defined inline within each datatype definition in the
    datatypes_conf.xml configuration file. These mappings determine which visualization plugin
    should be used by default when viewing datasets of a specific type.

    If no direct mapping exists for the extension, this method will walk up the inheritance
    chain to find a preferred visualization from a parent datatype class.

    Example configuration:
    <datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true">
        <visualization plugin="igv" />
    </datatype>
    """
    direct_mapping = datatypes_registry.visualization_mappings.get(datatype_extension)
    if direct_mapping:
        return direct_mapping

    current_datatype = datatypes_registry.get_datatype_by_extension(datatype_extension)
    if not current_datatype:
        return None

    # Use the same mapping approach as the datatypes API for consistency
    mapping_data = view_mapping(datatypes_registry)

    current_class_name = mapping_data.ext_to_class_name.get(datatype_extension)
    if not current_class_name:
        return None

    current_class_mappings = mapping_data.class_to_classes.get(current_class_name, {})

    for ext, visualization_mapping in datatypes_registry.visualization_mappings.items():
        if ext == datatype_extension:
            continue

        parent_class_name = mapping_data.ext_to_class_name.get(ext)
        if parent_class_name and parent_class_name in current_class_mappings:
            return visualization_mapping

    return None


__all__ = (
    "DatatypeConverterList",
    "DatatypeDetails",
@@ -179,4 +224,5 @@ __all__ = (
    "view_edam_formats",
    "view_edam_data",
    "view_visualization_mappings",
    "get_preferred_visualization",
)
+23 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ from fastapi import (
)

from galaxy.datatypes.registry import Registry
from galaxy.exceptions import ObjectNotFound
from galaxy.managers.datatypes import (
    DatatypeConverterList,
    DatatypeDetails,
@@ -25,6 +26,7 @@ from galaxy.managers.datatypes import (
    DatatypesEDAMDetailsDict,
    DatatypesMap,
    DatatypeVisualizationMappingsList,
    get_preferred_visualization,
    view_converters,
    view_edam_data,
    view_edam_formats,
@@ -33,8 +35,10 @@ from galaxy.managers.datatypes import (
    view_sniffers,
    view_visualization_mappings,
)
from galaxy.structured_app import StructuredApp
from . import (
    depends,
    DependsOnApp,
    Router,
)

@@ -64,6 +68,7 @@ IdentifierOnly: Optional[bool] = Query(
@router.cbv
class FastAPIDatatypes:
    datatypes_registry: Registry = depends(Registry)
    app: StructuredApp = DependsOnApp

    @router.get(
        "/api/datatypes",
@@ -253,11 +258,24 @@ class FastAPIDatatypes:
        if converters:
            result["converters"] = list(converters.keys())

        # Add preferred visualization if any
        preferred_viz = self.datatypes_registry.get_preferred_visualization(datatype)
        # Add preferred visualization if any and if the plugin is available
        preferred_viz = get_preferred_visualization(self.datatypes_registry, datatype)
        if preferred_viz:
            plugin_name = preferred_viz["visualization"]

            # Check if the visualization plugin is actually available
            try:
                if self.app.visualizations_registry:
                    self.app.visualizations_registry.get_plugin(plugin_name)
                    result["preferred_visualization"] = {
                "visualization": preferred_viz["visualization"],
                        "visualization": plugin_name,
                    }
                else:
                    log.warning(
                        f"Visualizations registry not available, skipping preferred visualization for '{datatype}'"
                    )
            except ObjectNotFound:
                # Plugin not available, don't include preferred_visualization
                log.warning(f"Preferred visualization '{plugin_name}' for datatype '{datatype}' is not available")

        return result