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.
@@ -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"}