mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
index carries the panel-entry contract; LazyTool serves it without materialising
ToolIndexEntry gains icon, xrefs, model_class, form_style and is_workflow_compatible, derived at populate time the same way Tool.__init__/to_dict derive them (tool_type class registry, root workflow_compatible attribute, page count). EDAM fields now go through expand_ontology_data, picking up the curated mapping overrides and legacy bio.tools xrefs the direct parse_edam_* calls missed. LazyTool.to_panel_entry emits the full client payload -- including tool_shed_repository (the field the panel-view integration tests assert), versions from the registered lineage, uuid, and the admin-gated config_file from source_path.
This commit is contained in:
@@ -37,6 +37,11 @@ class ToolIndexEntry:
|
||||
description: str = ""
|
||||
|
||||
# === Classification ===
|
||||
icon: str | None = None
|
||||
xrefs: list[dict[str, Any]] = field(default_factory=list)
|
||||
model_class: str = "Tool"
|
||||
form_style: str = "regular"
|
||||
is_workflow_compatible: bool = True
|
||||
panel_section_id: str | None = None
|
||||
panel_section_name: str | None = None
|
||||
labels: list[str] = field(default_factory=list)
|
||||
@@ -136,6 +141,11 @@ class ToolIndexEntry:
|
||||
"tool_shed_repository_id": self.tool_shed_repository_id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"icon": self.icon,
|
||||
"xrefs": self.xrefs,
|
||||
"model_class": self.model_class,
|
||||
"form_style": self.form_style,
|
||||
"is_workflow_compatible": self.is_workflow_compatible,
|
||||
"panel_section_id": self.panel_section_id,
|
||||
"panel_section_name": self.panel_section_name,
|
||||
"labels": self.labels,
|
||||
@@ -174,6 +184,11 @@ class ToolIndexEntry:
|
||||
tool_shed_repository_id=data.get("tool_shed_repository_id"),
|
||||
name=data.get("name", ""),
|
||||
description=data.get("description", ""),
|
||||
icon=data.get("icon"),
|
||||
xrefs=data.get("xrefs", []),
|
||||
model_class=data.get("model_class", "Tool"),
|
||||
form_style=data.get("form_style", "regular"),
|
||||
is_workflow_compatible=data.get("is_workflow_compatible", True),
|
||||
panel_section_id=data.get("panel_section_id"),
|
||||
panel_section_name=data.get("panel_section_name"),
|
||||
labels=data.get("labels", []),
|
||||
|
||||
@@ -376,6 +376,7 @@ def build_index_entry_from_source(
|
||||
discovered,
|
||||
stored,
|
||||
tool_source,
|
||||
biotools_metadata_source=None,
|
||||
):
|
||||
"""Assemble a :class:`ToolIndexEntry` from a populator triple.
|
||||
|
||||
@@ -428,19 +429,48 @@ def build_index_entry_from_source(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
edam_operations: list[str] = []
|
||||
if hasattr(tool_source, "parse_edam_operations"):
|
||||
try:
|
||||
edam_operations = list(tool_source.parse_edam_operations() or ())
|
||||
except Exception:
|
||||
pass
|
||||
lowered = tool_id.lower()
|
||||
all_ids = [lowered]
|
||||
if "/repos/" in lowered:
|
||||
all_ids = [lowered, lowered.rsplit("/", 1)[0], lowered.rsplit("/", 2)[-2]]
|
||||
# Same ontology expansion as ``Tool.__init__`` — curated EDAM mapping
|
||||
# overrides and legacy bio.tools xrefs included.
|
||||
from galaxy.tool_util.ontologies.ontology_data import expand_ontology_data
|
||||
|
||||
edam_topics: list[str] = []
|
||||
if hasattr(tool_source, "parse_edam_topics"):
|
||||
try:
|
||||
edam_topics = list(tool_source.parse_edam_topics() or ())
|
||||
except Exception:
|
||||
pass
|
||||
ontology_data = expand_ontology_data(tool_source, all_ids, biotools_metadata_source)
|
||||
edam_operations = list(ontology_data.edam_operations or ())
|
||||
edam_topics = list(ontology_data.edam_topics or ())
|
||||
xrefs: list[dict[str, Any]] = [dict(x) for x in ontology_data.xrefs or ()]
|
||||
|
||||
icon = tool_source.parse_icon() if hasattr(tool_source, "parse_icon") else None
|
||||
|
||||
# ``model_class`` / ``form_style`` mirror ``Tool.to_dict``'s ad-hoc
|
||||
# class inspection via the same tool_type registry the eager path
|
||||
# constructs tools with. Local import: galaxy.tools is the full tool
|
||||
# machinery; only the registry mapping is needed.
|
||||
from galaxy.tools import (
|
||||
DatabaseOperationTool,
|
||||
InteractiveTool,
|
||||
Tool,
|
||||
tool_types,
|
||||
)
|
||||
|
||||
tool_class = tool_types.get(tool_type, Tool)
|
||||
regular_form = tool_class is Tool or issubclass(tool_class, (DatabaseOperationTool, InteractiveTool))
|
||||
|
||||
# ``Tool.check_workflow_compatible`` equivalents derivable at parse
|
||||
# time: multi-page tools and data sources are incompatible; XML tools
|
||||
# may opt out via workflow_compatible="false" on the root.
|
||||
is_workflow_compatible = not tool_type.startswith("data_source")
|
||||
try:
|
||||
pages = tool_source.parse_input_pages()
|
||||
if pages is not None and len(pages.page_sources) > 1:
|
||||
is_workflow_compatible = False
|
||||
except Exception:
|
||||
pass
|
||||
root = getattr(tool_source, "root", None)
|
||||
if root is not None and str(root.get("workflow_compatible", "True")).lower() in ("false", "0", "no"):
|
||||
is_workflow_compatible = False
|
||||
|
||||
# Honour the same version-default rules as ``Tool.__init__``: empty
|
||||
# ``version`` on a pre-16.04-profile tool becomes "1.0.0"; on newer
|
||||
@@ -461,6 +491,11 @@ def build_index_entry_from_source(
|
||||
panel_section_id=discovered.section_id,
|
||||
panel_section_name=discovered.section_name,
|
||||
labels=list(discovered.labels or ()),
|
||||
icon=icon,
|
||||
xrefs=xrefs,
|
||||
model_class=tool_class.__name__,
|
||||
form_style="regular" if regular_form else "special",
|
||||
is_workflow_compatible=is_workflow_compatible,
|
||||
edam_operations=edam_operations,
|
||||
edam_topics=edam_topics,
|
||||
source_hash=stored.hash,
|
||||
@@ -738,6 +773,11 @@ def populate_store_inline(
|
||||
# post-walk syncs (_stamp_panel_sections_onto_index,
|
||||
# _sync_tool_mutations_to_index) replaced ad-hoc.
|
||||
full_scan = paths is None or prune
|
||||
# Local import: galaxy.tools.biotools executes the galaxy.tools
|
||||
# package; only the metadata-source factory is needed, once per run.
|
||||
from galaxy.tools.biotools import get_galaxy_biotools_metadata_source
|
||||
|
||||
biotools_metadata_source = get_galaxy_biotools_metadata_source(config)
|
||||
for store_name in sorted(writable_names):
|
||||
triples = parsed_per_store[store_name]
|
||||
if full_scan:
|
||||
@@ -749,7 +789,7 @@ def populate_store_inline(
|
||||
# stays as-is (use reconcile_index for full prune).
|
||||
index = stores[store_name].load_index() or ToolIndex()
|
||||
for d, stored, tool_source in triples:
|
||||
entry = build_index_entry_from_source(d, stored, tool_source)
|
||||
entry = build_index_entry_from_source(d, stored, tool_source, biotools_metadata_source)
|
||||
if entry is not None:
|
||||
index.add_entry(entry)
|
||||
try:
|
||||
|
||||
@@ -173,6 +173,9 @@ class LazyTool:
|
||||
require_login = _entry_attr("require_login")
|
||||
edam_operations = _entry_attr("edam_operations")
|
||||
edam_topics = _entry_attr("edam_topics")
|
||||
icon = _entry_attr("icon")
|
||||
xrefs = _entry_attr("xrefs")
|
||||
is_workflow_compatible = _entry_attr("is_workflow_compatible")
|
||||
|
||||
# --- forwarded mutable entry surface ---
|
||||
# Eager ``_load_tool_tag_set`` (base.py:964-987) mutates these post-create.
|
||||
@@ -317,20 +320,42 @@ class LazyTool:
|
||||
parse every tool on the first request.
|
||||
"""
|
||||
entry = self._entry
|
||||
return {
|
||||
if self._lineage is not None:
|
||||
versions = list(self._lineage.tool_versions)
|
||||
else:
|
||||
versions = [entry.version] if entry.version else []
|
||||
payload = {
|
||||
"model_class": entry.model_class,
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"version": self.version,
|
||||
"description": self.description,
|
||||
"labels": self.labels if self.labels else [],
|
||||
"icon": entry.icon,
|
||||
"edam_operations": entry.edam_operations or [],
|
||||
"edam_topics": entry.edam_topics or [],
|
||||
"hidden": self.hidden,
|
||||
"model_class": "Tool",
|
||||
"is_workflow_compatible": entry.is_workflow_compatible,
|
||||
"xrefs": entry.xrefs or [],
|
||||
"versions": versions,
|
||||
"hidden_versions": [],
|
||||
"link": f"/tool_runner?tool_id={self.id}",
|
||||
"panel_section_id": entry.panel_section_id,
|
||||
"panel_section_name": entry.panel_section_name,
|
||||
"link": f"/tool_runner?tool_id={self.id}",
|
||||
"form_style": entry.form_style,
|
||||
}
|
||||
if entry.uuid:
|
||||
payload["uuid"] = entry.uuid
|
||||
if entry.tool_shed:
|
||||
payload["tool_shed_repository"] = {
|
||||
"name": entry.repository_name,
|
||||
"owner": entry.repository_owner,
|
||||
"changeset_revision": entry.changeset_revision,
|
||||
"tool_shed": entry.tool_shed,
|
||||
}
|
||||
if trans is not None and getattr(trans, "user_is_admin", False):
|
||||
payload["config_file"] = entry.source_path
|
||||
return payload
|
||||
|
||||
def to_dict(self, trans=None, link_details: bool = False, tool_help: bool = False, **kw) -> dict[str, Any]:
|
||||
"""Materialise and delegate — ``/api/tools/{id}`` contract.
|
||||
|
||||
@@ -133,6 +133,28 @@ def test_tool_tags_answered_without_materialise():
|
||||
assert t.tool_tags == ["curated"]
|
||||
|
||||
|
||||
def test_to_panel_entry_carries_client_contract_fields():
|
||||
e = _entry(
|
||||
id="toolshed.example.com/repos/owner/repo/tool/1.0",
|
||||
tool_shed="toolshed.example.com",
|
||||
repository_name="repo",
|
||||
repository_owner="owner",
|
||||
changeset_revision="abc123",
|
||||
model_class="Tool",
|
||||
form_style="regular",
|
||||
is_workflow_compatible=True,
|
||||
xrefs=[{"value": "bwa", "reftype": "bio.tools"}],
|
||||
)
|
||||
t = _stub(e)
|
||||
d = t.to_panel_entry(trans=None)
|
||||
assert d["is_workflow_compatible"] is True
|
||||
assert d["form_style"] == "regular"
|
||||
assert d["xrefs"] == [{"value": "bwa", "reftype": "bio.tools"}]
|
||||
assert d["versions"] == ["2.5.0"]
|
||||
assert d["tool_shed_repository"]["changeset_revision"] == "abc123"
|
||||
assert "config_file" not in d
|
||||
|
||||
|
||||
def test_to_dict_materialises():
|
||||
calls: list[Any] = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user