galaxy-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli_anything/galaxy/__init__.py +3 -0
- cli_anything/galaxy/__main__.py +5 -0
- cli_anything/galaxy/core/__init__.py +0 -0
- cli_anything/galaxy/core/config.py +39 -0
- cli_anything/galaxy/core/dataset.py +100 -0
- cli_anything/galaxy/core/history.py +98 -0
- cli_anything/galaxy/core/invocation.py +79 -0
- cli_anything/galaxy/core/job.py +88 -0
- cli_anything/galaxy/core/library.py +63 -0
- cli_anything/galaxy/core/session.py +103 -0
- cli_anything/galaxy/core/tool.py +176 -0
- cli_anything/galaxy/core/workflow.py +131 -0
- cli_anything/galaxy/galaxy_cli.py +882 -0
- cli_anything/galaxy/skills/SKILL.md +165 -0
- cli_anything/galaxy/tests/__init__.py +0 -0
- cli_anything/galaxy/tests/test_core.py +712 -0
- cli_anything/galaxy/tests/test_full_e2e.py +313 -0
- cli_anything/galaxy/utils/__init__.py +0 -0
- cli_anything/galaxy/utils/galaxy_backend.py +266 -0
- cli_anything/galaxy/utils/repl_skin.py +521 -0
- galaxy_cli-1.0.0.dist-info/METADATA +90 -0
- galaxy_cli-1.0.0.dist-info/RECORD +26 -0
- galaxy_cli-1.0.0.dist-info/WHEEL +5 -0
- galaxy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- galaxy_cli-1.0.0.dist-info/licenses/LICENSE +22 -0
- galaxy_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Tool management — list, search, show, run Galaxy tools."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _searchable_text(tool):
|
|
5
|
+
return " ".join(
|
|
6
|
+
[
|
|
7
|
+
tool.get("id", ""),
|
|
8
|
+
tool.get("name", ""),
|
|
9
|
+
tool.get("description", ""),
|
|
10
|
+
tool.get("section", ""),
|
|
11
|
+
]
|
|
12
|
+
).lower()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _normalize_tool_dict(tool, section_id=None):
|
|
16
|
+
if section_id and tool.get("panel_section_id") != section_id:
|
|
17
|
+
return None
|
|
18
|
+
return {
|
|
19
|
+
"id": tool.get("id", ""),
|
|
20
|
+
"name": tool.get("name", ""),
|
|
21
|
+
"version": tool.get("version", ""),
|
|
22
|
+
"description": tool.get("description", ""),
|
|
23
|
+
"section": tool.get("panel_section_name", ""),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_search_hits(client, tools, section_id=None):
|
|
28
|
+
"""Resolve string-only search hits into full tool records."""
|
|
29
|
+
if not tools or not all(isinstance(tool, str) for tool in tools):
|
|
30
|
+
return tools
|
|
31
|
+
|
|
32
|
+
all_tools = client.get("tools", params={"in_panel": False})
|
|
33
|
+
by_id = {}
|
|
34
|
+
for tool in all_tools:
|
|
35
|
+
if isinstance(tool, dict) and tool.get("id"):
|
|
36
|
+
by_id[tool["id"]] = tool
|
|
37
|
+
|
|
38
|
+
resolved = []
|
|
39
|
+
for tool_id in tools:
|
|
40
|
+
tool = by_id.get(tool_id)
|
|
41
|
+
if tool is None:
|
|
42
|
+
continue
|
|
43
|
+
if section_id and tool.get("panel_section_id") != section_id:
|
|
44
|
+
continue
|
|
45
|
+
resolved.append(tool)
|
|
46
|
+
return resolved
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def list_tools(client, query=None, section_id=None):
|
|
50
|
+
"""List available tools, optionally filtered by search or section."""
|
|
51
|
+
params = {"in_panel": False}
|
|
52
|
+
if query:
|
|
53
|
+
params["q"] = query
|
|
54
|
+
tools = client.get("tools", params=params)
|
|
55
|
+
tools = _resolve_search_hits(client, tools, section_id=section_id)
|
|
56
|
+
results = []
|
|
57
|
+
for t in tools:
|
|
58
|
+
if isinstance(t, dict):
|
|
59
|
+
normalized = _normalize_tool_dict(t, section_id=section_id)
|
|
60
|
+
if normalized is not None:
|
|
61
|
+
results.append(normalized)
|
|
62
|
+
return results
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def search_tools(client, query):
|
|
66
|
+
"""Search tools by name or description."""
|
|
67
|
+
query = (query or "").strip()
|
|
68
|
+
if not query:
|
|
69
|
+
return []
|
|
70
|
+
|
|
71
|
+
# First try Galaxy's native search behavior.
|
|
72
|
+
direct_results = list_tools(client, query=query)
|
|
73
|
+
if direct_results:
|
|
74
|
+
return direct_results
|
|
75
|
+
|
|
76
|
+
terms = [term.lower() for term in query.split() if term.strip()]
|
|
77
|
+
if len(terms) <= 1:
|
|
78
|
+
return direct_results
|
|
79
|
+
|
|
80
|
+
# Fallback: keyword AND matching across ID, name, description, and section.
|
|
81
|
+
all_tools = list_tools(client)
|
|
82
|
+
ranked = []
|
|
83
|
+
for tool in all_tools:
|
|
84
|
+
haystack = _searchable_text(tool)
|
|
85
|
+
if all(term in haystack for term in terms):
|
|
86
|
+
score = 0
|
|
87
|
+
name = tool.get("name", "").lower()
|
|
88
|
+
description = tool.get("description", "").lower()
|
|
89
|
+
tool_id = tool.get("id", "").lower()
|
|
90
|
+
for term in terms:
|
|
91
|
+
if term in name:
|
|
92
|
+
score += 3
|
|
93
|
+
if term in description:
|
|
94
|
+
score += 2
|
|
95
|
+
if term in tool_id:
|
|
96
|
+
score += 2
|
|
97
|
+
ranked.append((score, tool))
|
|
98
|
+
|
|
99
|
+
ranked.sort(key=lambda item: (-item[0], item[1].get("name", ""), item[1].get("id", "")))
|
|
100
|
+
return [tool for _, tool in ranked]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def show_tool(client, tool_id):
|
|
104
|
+
"""Show detailed info about a tool, including its inputs."""
|
|
105
|
+
info = client.get(f"tools/{tool_id}", params={"io_details": True})
|
|
106
|
+
inputs = []
|
|
107
|
+
for inp in info.get("inputs", []):
|
|
108
|
+
inputs.append({
|
|
109
|
+
"name": inp.get("name", ""),
|
|
110
|
+
"label": inp.get("label", ""),
|
|
111
|
+
"type": inp.get("type", ""),
|
|
112
|
+
"value": inp.get("value", ""),
|
|
113
|
+
"optional": inp.get("optional", False),
|
|
114
|
+
"help": inp.get("help", ""),
|
|
115
|
+
})
|
|
116
|
+
outputs = []
|
|
117
|
+
for out in info.get("outputs", []):
|
|
118
|
+
outputs.append({
|
|
119
|
+
"name": out.get("name", ""),
|
|
120
|
+
"format": out.get("format", ""),
|
|
121
|
+
"label": out.get("label", ""),
|
|
122
|
+
})
|
|
123
|
+
return {
|
|
124
|
+
"id": info.get("id", tool_id),
|
|
125
|
+
"name": info.get("name", ""),
|
|
126
|
+
"version": info.get("version", ""),
|
|
127
|
+
"description": info.get("description", ""),
|
|
128
|
+
"edam_topics": info.get("edam_topics", []),
|
|
129
|
+
"edam_operations": info.get("edam_operations", []),
|
|
130
|
+
"requirements": [
|
|
131
|
+
{"name": r.get("name", ""), "version": r.get("version", "")}
|
|
132
|
+
for r in info.get("requirements", [])
|
|
133
|
+
],
|
|
134
|
+
"inputs": inputs,
|
|
135
|
+
"outputs": outputs,
|
|
136
|
+
"help": info.get("help", ""),
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def run_tool(client, tool_id, history_id, inputs=None):
|
|
141
|
+
"""Run a tool with given inputs in a history.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
client: GalaxyClient instance.
|
|
145
|
+
tool_id: Tool identifier.
|
|
146
|
+
history_id: Target history for outputs.
|
|
147
|
+
inputs: Dict of tool parameter name -> value.
|
|
148
|
+
"""
|
|
149
|
+
payload = {
|
|
150
|
+
"tool_id": tool_id,
|
|
151
|
+
"history_id": history_id,
|
|
152
|
+
"inputs": inputs or {},
|
|
153
|
+
}
|
|
154
|
+
result = client.post("tools", json_data=payload)
|
|
155
|
+
jobs = result.get("jobs", [])
|
|
156
|
+
outputs = result.get("outputs", [])
|
|
157
|
+
return {
|
|
158
|
+
"tool_id": tool_id,
|
|
159
|
+
"history_id": history_id,
|
|
160
|
+
"jobs": [
|
|
161
|
+
{
|
|
162
|
+
"id": j.get("id", ""),
|
|
163
|
+
"state": j.get("state", ""),
|
|
164
|
+
"tool_id": j.get("tool_id", ""),
|
|
165
|
+
}
|
|
166
|
+
for j in jobs
|
|
167
|
+
],
|
|
168
|
+
"outputs": [
|
|
169
|
+
{
|
|
170
|
+
"id": o.get("id", ""),
|
|
171
|
+
"name": o.get("name", ""),
|
|
172
|
+
"extension": o.get("extension", ""),
|
|
173
|
+
}
|
|
174
|
+
for o in outputs
|
|
175
|
+
],
|
|
176
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Workflow management — import, export, list, show, run, delete workflows."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def list_workflows(client, published=False):
|
|
8
|
+
"""List workflows for the current user."""
|
|
9
|
+
params = {}
|
|
10
|
+
if published:
|
|
11
|
+
params["show_published"] = True
|
|
12
|
+
workflows = client.get("workflows", params=params)
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
"id": w.get("id", ""),
|
|
16
|
+
"name": w.get("name", ""),
|
|
17
|
+
"owner": w.get("owner", ""),
|
|
18
|
+
"published": w.get("published", False),
|
|
19
|
+
"deleted": w.get("deleted", False),
|
|
20
|
+
"step_count": w.get("number_of_steps", 0),
|
|
21
|
+
"update_time": w.get("update_time", ""),
|
|
22
|
+
"tags": w.get("tags", []),
|
|
23
|
+
}
|
|
24
|
+
for w in workflows
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def show_workflow(client, workflow_id):
|
|
29
|
+
"""Show detailed info about a workflow."""
|
|
30
|
+
info = client.get(f"workflows/{workflow_id}")
|
|
31
|
+
steps = {}
|
|
32
|
+
for step_id, step in info.get("steps", {}).items():
|
|
33
|
+
steps[step_id] = {
|
|
34
|
+
"id": step.get("id", ""),
|
|
35
|
+
"type": step.get("type", ""),
|
|
36
|
+
"tool_id": step.get("tool_id"),
|
|
37
|
+
"label": step.get("label", ""),
|
|
38
|
+
"annotation": step.get("annotation", ""),
|
|
39
|
+
"input_connections": step.get("input_connections", {}),
|
|
40
|
+
}
|
|
41
|
+
inputs = {}
|
|
42
|
+
for inp_id, inp in info.get("inputs", {}).items():
|
|
43
|
+
inputs[inp_id] = {
|
|
44
|
+
"label": inp.get("label", ""),
|
|
45
|
+
"value": inp.get("value", ""),
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
"id": info.get("id", workflow_id),
|
|
49
|
+
"name": info.get("name", ""),
|
|
50
|
+
"owner": info.get("owner", ""),
|
|
51
|
+
"annotation": info.get("annotation", ""),
|
|
52
|
+
"published": info.get("published", False),
|
|
53
|
+
"step_count": len(steps),
|
|
54
|
+
"steps": steps,
|
|
55
|
+
"inputs": inputs,
|
|
56
|
+
"tags": info.get("tags", []),
|
|
57
|
+
"version": info.get("version", 0),
|
|
58
|
+
"update_time": info.get("update_time", ""),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def import_workflow(client, workflow_path=None, workflow_dict=None):
|
|
63
|
+
"""Import a workflow from a file or dict."""
|
|
64
|
+
if workflow_path:
|
|
65
|
+
path = Path(workflow_path)
|
|
66
|
+
if not path.exists():
|
|
67
|
+
raise FileNotFoundError(f"Workflow file not found: {workflow_path}")
|
|
68
|
+
workflow_dict = json.loads(path.read_text())
|
|
69
|
+
if not workflow_dict:
|
|
70
|
+
raise ValueError("Either workflow_path or workflow_dict must be provided")
|
|
71
|
+
result = client.post("workflows", json_data={"workflow": workflow_dict})
|
|
72
|
+
return {
|
|
73
|
+
"id": result.get("id", ""),
|
|
74
|
+
"name": result.get("name", ""),
|
|
75
|
+
"status": "imported",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def export_workflow(client, workflow_id, output_path=None):
|
|
80
|
+
"""Export a workflow to Galaxy format JSON."""
|
|
81
|
+
info = client.get(f"workflows/{workflow_id}/download")
|
|
82
|
+
if output_path:
|
|
83
|
+
path = Path(output_path)
|
|
84
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
path.write_text(json.dumps(info, indent=2))
|
|
86
|
+
return {"id": workflow_id, "output": str(path), "status": "exported"}
|
|
87
|
+
return info
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def run_workflow(client, workflow_id, history_id=None, inputs=None, params=None,
|
|
91
|
+
new_history_name=None):
|
|
92
|
+
"""Run a workflow.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
client: GalaxyClient instance.
|
|
96
|
+
workflow_id: Workflow ID to run.
|
|
97
|
+
history_id: Existing history to put outputs in.
|
|
98
|
+
inputs: Dict mapping step input labels/indices to dataset IDs.
|
|
99
|
+
params: Dict mapping step indices to parameter overrides.
|
|
100
|
+
new_history_name: If set, create a new history with this name for outputs.
|
|
101
|
+
"""
|
|
102
|
+
payload = {"workflow_id": workflow_id}
|
|
103
|
+
if new_history_name:
|
|
104
|
+
payload["history"] = f"hist_name={new_history_name}"
|
|
105
|
+
elif history_id:
|
|
106
|
+
payload["history_id"] = history_id
|
|
107
|
+
|
|
108
|
+
if inputs:
|
|
109
|
+
# Convert simple {step_index: dataset_id} to Galaxy API format
|
|
110
|
+
ds_map = {}
|
|
111
|
+
for step_key, dataset_id in inputs.items():
|
|
112
|
+
ds_map[str(step_key)] = {"src": "hda", "id": dataset_id}
|
|
113
|
+
payload["ds_map"] = ds_map
|
|
114
|
+
|
|
115
|
+
if params:
|
|
116
|
+
payload["parameters"] = params
|
|
117
|
+
|
|
118
|
+
result = client.post(f"workflows/{workflow_id}/invocations", json_data=payload)
|
|
119
|
+
return {
|
|
120
|
+
"id": result.get("id", ""),
|
|
121
|
+
"workflow_id": workflow_id,
|
|
122
|
+
"history_id": result.get("history_id", history_id or ""),
|
|
123
|
+
"state": result.get("state", ""),
|
|
124
|
+
"status": "invoked",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def delete_workflow(client, workflow_id):
|
|
129
|
+
"""Delete a workflow."""
|
|
130
|
+
client.delete(f"workflows/{workflow_id}")
|
|
131
|
+
return {"id": workflow_id, "status": "deleted"}
|