scmcp-shared 0.2.5__py3-none-any.whl → 0.3.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.
- scmcp_shared/__init__.py +1 -1
- scmcp_shared/cli.py +96 -0
- scmcp_shared/schema/__init__.py +9 -0
- scmcp_shared/schema/io.py +0 -4
- scmcp_shared/schema/tl.py +0 -5
- scmcp_shared/server/__init__.py +11 -49
- scmcp_shared/server/base.py +153 -0
- scmcp_shared/server/io.py +70 -70
- scmcp_shared/server/pl.py +287 -338
- scmcp_shared/server/pp.py +319 -373
- scmcp_shared/server/tl.py +392 -441
- scmcp_shared/server/util.py +240 -250
- scmcp_shared/util.py +82 -17
- {scmcp_shared-0.2.5.dist-info → scmcp_shared-0.3.0.dist-info}/METADATA +1 -1
- scmcp_shared-0.3.0.dist-info/RECORD +21 -0
- scmcp_shared-0.2.5.dist-info/RECORD +0 -19
- {scmcp_shared-0.2.5.dist-info → scmcp_shared-0.3.0.dist-info}/WHEEL +0 -0
- {scmcp_shared-0.2.5.dist-info → scmcp_shared-0.3.0.dist-info}/licenses/LICENSE +0 -0
scmcp_shared/server/pp.py
CHANGED
@@ -1,379 +1,325 @@
|
|
1
|
-
|
2
1
|
import os
|
3
2
|
import inspect
|
4
3
|
import scanpy as sc
|
5
|
-
from fastmcp import FastMCP
|
4
|
+
from fastmcp import FastMCP, Context
|
6
5
|
from fastmcp.exceptions import ToolError
|
7
6
|
from ..schema.pp import *
|
8
|
-
from ..schema import
|
7
|
+
from ..schema import AdataInfo
|
9
8
|
from ..util import filter_args, add_op_log, forward_request, get_ads, generate_msg
|
10
|
-
from
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
)
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
):
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
)
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
)
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
)
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
@pp_mcp.tool()
|
328
|
-
async def scrublet(
|
329
|
-
request: ScrubletModel = ScrubletModel(),
|
330
|
-
adinfo: AdataModel = AdataModel()
|
331
|
-
):
|
332
|
-
"""Predict doublets using Scrublet"""
|
333
|
-
|
334
|
-
try:
|
335
|
-
result = await forward_request("pp_scrublet", request, adinfo)
|
336
|
-
if result is not None:
|
337
|
-
return result
|
338
|
-
func_kwargs = filter_args(request, sc.pp.scrublet)
|
339
|
-
ads = get_ads()
|
340
|
-
adata = ads.get_adata(adinfo=adinfo)
|
341
|
-
sc.pp.scrublet(adata, **func_kwargs)
|
342
|
-
add_op_log(adata, sc.pp.scrublet, func_kwargs, adinfo)
|
343
|
-
return [
|
344
|
-
generate_msg(adinfo, adata, ads)
|
345
|
-
]
|
346
|
-
except ToolError as e:
|
347
|
-
raise ToolError(e)
|
348
|
-
except Exception as e:
|
349
|
-
if hasattr(e, '__context__') and e.__context__:
|
350
|
-
raise ToolError(e.__context__)
|
351
|
-
else:
|
352
|
-
raise ToolError(e)
|
353
|
-
|
354
|
-
@pp_mcp.tool()
|
355
|
-
async def neighbors(
|
356
|
-
request: NeighborsModel = NeighborsModel(),
|
357
|
-
adinfo: AdataModel = AdataModel()
|
358
|
-
):
|
359
|
-
"""Compute nearest neighbors distance matrix and neighborhood graph"""
|
360
|
-
|
361
|
-
try:
|
362
|
-
result = await forward_request("pp_neighbors", request, adinfo)
|
363
|
-
if result is not None:
|
364
|
-
return result
|
365
|
-
func_kwargs = filter_args(request, sc.pp.neighbors)
|
366
|
-
ads = get_ads()
|
367
|
-
adata = ads.get_adata(adinfo=adinfo)
|
368
|
-
sc.pp.neighbors(adata, **func_kwargs)
|
369
|
-
add_op_log(adata, sc.pp.neighbors, func_kwargs, adinfo)
|
370
|
-
return [
|
371
|
-
generate_msg(adinfo, adata, ads)
|
372
|
-
]
|
373
|
-
except ToolError as e:
|
374
|
-
raise ToolError(e)
|
375
|
-
except Exception as e:
|
376
|
-
if hasattr(e, '__context__') and e.__context__:
|
377
|
-
raise ToolError(e.__context__)
|
378
|
-
else:
|
379
|
-
raise ToolError(e)
|
9
|
+
from .base import BaseMCP
|
10
|
+
|
11
|
+
|
12
|
+
class ScanpyPreprocessingMCP(BaseMCP):
|
13
|
+
def __init__(self, include_tools: list = None, exclude_tools: list = None, AdataInfo: AdataInfo = AdataInfo):
|
14
|
+
"""
|
15
|
+
Initialize ScanpyPreprocessingMCP with optional tool filtering.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
include_tools (list, optional): List of tool names to include. If None, all tools are included.
|
19
|
+
exclude_tools (list, optional): List of tool names to exclude. If None, no tools are excluded.
|
20
|
+
AdataInfo: The AdataInfo class to use for type annotations.
|
21
|
+
"""
|
22
|
+
super().__init__("ScanpyMCP-PP-Server", include_tools, exclude_tools, AdataInfo)
|
23
|
+
|
24
|
+
def _tool_subset_cells(self):
|
25
|
+
def _subset_cells(request: SubsetCellModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
26
|
+
"""filter or subset cells based on total genes expressed counts and numbers. or values in adata.obs[obs_key]"""
|
27
|
+
try:
|
28
|
+
result = forward_request("subset_cells", request, adinfo)
|
29
|
+
if result is not None:
|
30
|
+
return result
|
31
|
+
|
32
|
+
ads = get_ads()
|
33
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
34
|
+
func_kwargs = filter_args(request, sc.pp.filter_cells)
|
35
|
+
if func_kwargs:
|
36
|
+
sc.pp.filter_cells(adata, **func_kwargs)
|
37
|
+
add_op_log(adata, sc.pp.filter_cells, func_kwargs, adinfo)
|
38
|
+
# Subset based on obs (cells) criteria
|
39
|
+
if request.obs_key is not None:
|
40
|
+
if request.obs_key not in adata.obs.columns:
|
41
|
+
raise ValueError(f"Key '{request.obs_key}' not found in adata.obs")
|
42
|
+
mask = True # Start with all cells selected
|
43
|
+
if request.obs_value is not None:
|
44
|
+
mask = mask & (adata.obs[request.obs_key] == request.obs_value)
|
45
|
+
if request.obs_min is not None:
|
46
|
+
mask = mask & (adata.obs[request.obs_key] >= request.obs_min)
|
47
|
+
if request.obs_max is not None:
|
48
|
+
mask = mask & (adata.obs[request.obs_key] <= request.obs_max)
|
49
|
+
adata = adata[mask, :]
|
50
|
+
add_op_log(adata, "subset_cells",
|
51
|
+
{
|
52
|
+
"obs_key": request.obs_key, "obs_value": request.obs_value,
|
53
|
+
"obs_min": request.obs_min, "obs_max": request.obs_max
|
54
|
+
}, adinfo
|
55
|
+
)
|
56
|
+
ads.set_adata(adata, adinfo=adinfo)
|
57
|
+
return [generate_msg(adinfo, adata, ads)]
|
58
|
+
except ToolError as e:
|
59
|
+
raise ToolError(e)
|
60
|
+
except Exception as e:
|
61
|
+
if hasattr(e, '__context__') and e.__context__:
|
62
|
+
raise ToolError(e.__context__)
|
63
|
+
else:
|
64
|
+
raise ToolError(e)
|
65
|
+
return _subset_cells
|
66
|
+
|
67
|
+
def _tool_subset_genes(self):
|
68
|
+
def _subset_genes(request: SubsetGeneModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
69
|
+
"""filter or subset genes based on number of cells or counts, or values in adata.var[var_key] or subset highly variable genes"""
|
70
|
+
try:
|
71
|
+
result = forward_request("pp_subset_genes", request, adinfo)
|
72
|
+
if result is not None:
|
73
|
+
return result
|
74
|
+
func_kwargs = filter_args(request, sc.pp.filter_genes)
|
75
|
+
ads = get_ads()
|
76
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
77
|
+
if func_kwargs:
|
78
|
+
sc.pp.filter_genes(adata, **func_kwargs)
|
79
|
+
add_op_log(adata, sc.pp.filter_genes, func_kwargs, adinfo)
|
80
|
+
if request.var_key is not None:
|
81
|
+
if request.var_key not in adata.var.columns:
|
82
|
+
raise ValueError(f"Key '{request.var_key}' not found in adata.var")
|
83
|
+
mask = True # Start with all genes selected
|
84
|
+
if request.var_min is not None:
|
85
|
+
mask = mask & (adata.var[request.var_key] >= request.var_min)
|
86
|
+
if request.var_max is not None:
|
87
|
+
mask = mask & (adata.var[request.var_key] <= request.var_max)
|
88
|
+
adata = adata[:, mask]
|
89
|
+
if request.highly_variable is not None:
|
90
|
+
adata = adata[:, adata.var.highly_variable]
|
91
|
+
add_op_log(adata, "subset_genes",
|
92
|
+
{
|
93
|
+
"var_key": request.var_key, "var_value": request.var_value,
|
94
|
+
"var_min": request.var_min, "var_max": request.var_max, "hpv": request.highly_variable
|
95
|
+
}, adinfo
|
96
|
+
)
|
97
|
+
ads.set_adata(adata, adinfo=adinfo)
|
98
|
+
return [generate_msg(adinfo, adata, ads)]
|
99
|
+
except ToolError as e:
|
100
|
+
raise ToolError(e)
|
101
|
+
except Exception as e:
|
102
|
+
if hasattr(e, '__context__') and e.__context__:
|
103
|
+
raise ToolError(e.__context__)
|
104
|
+
else:
|
105
|
+
raise ToolError(e)
|
106
|
+
return _subset_genes
|
107
|
+
|
108
|
+
def _tool_calculate_qc_metrics(self):
|
109
|
+
def _calculate_qc_metrics(request: CalculateQCMetrics, adinfo: self.AdataInfo=self.AdataInfo()):
|
110
|
+
"""Calculate quality control metrics(common metrics: total counts, gene number, percentage of counts in ribosomal and mitochondrial) for AnnData."""
|
111
|
+
try:
|
112
|
+
result = forward_request("pp_calculate_qc_metrics", request, adinfo)
|
113
|
+
if result is not None:
|
114
|
+
return result
|
115
|
+
|
116
|
+
func_kwargs = filter_args(request, sc.pp.calculate_qc_metrics)
|
117
|
+
ads = get_ads()
|
118
|
+
adata = ads.get_adata(adinfo=adinfo)
|
119
|
+
func_kwargs["inplace"] = True
|
120
|
+
try:
|
121
|
+
sc.pp.calculate_qc_metrics(adata, **func_kwargs)
|
122
|
+
add_op_log(adata, sc.pp.calculate_qc_metrics, func_kwargs, adinfo)
|
123
|
+
except KeyError as e:
|
124
|
+
raise KeyError(f"Cound find {e} in adata.var")
|
125
|
+
return [generate_msg(adinfo, adata, ads)]
|
126
|
+
except ToolError as e:
|
127
|
+
raise ToolError(e)
|
128
|
+
except Exception as e:
|
129
|
+
if hasattr(e, '__context__') and e.__context__:
|
130
|
+
raise ToolError(e.__context__)
|
131
|
+
else:
|
132
|
+
raise ToolError(e)
|
133
|
+
return _calculate_qc_metrics
|
134
|
+
|
135
|
+
def _tool_log1p(self):
|
136
|
+
def _log1p(request: Log1PModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
137
|
+
"""Logarithmize the data matrix"""
|
138
|
+
try:
|
139
|
+
result = forward_request("pp_log1p", request, adinfo)
|
140
|
+
if result is not None:
|
141
|
+
return result
|
142
|
+
func_kwargs = filter_args(request, sc.pp.log1p)
|
143
|
+
ads = get_ads()
|
144
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
145
|
+
try:
|
146
|
+
sc.pp.log1p(adata, **func_kwargs)
|
147
|
+
adata.raw = adata.copy()
|
148
|
+
add_op_log(adata, sc.pp.log1p, func_kwargs, adinfo)
|
149
|
+
except Exception as e:
|
150
|
+
raise e
|
151
|
+
ads.set_adata(adata, adinfo=adinfo)
|
152
|
+
return [generate_msg(adinfo, adata, ads)]
|
153
|
+
except ToolError as e:
|
154
|
+
raise ToolError(e)
|
155
|
+
except Exception as e:
|
156
|
+
if hasattr(e, '__context__') and e.__context__:
|
157
|
+
raise ToolError(e.__context__)
|
158
|
+
else:
|
159
|
+
raise ToolError(e)
|
160
|
+
return _log1p
|
161
|
+
|
162
|
+
def _tool_normalize_total(self):
|
163
|
+
def _normalize_total(request: NormalizeTotalModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
164
|
+
"""Normalize counts per cell to the same total count"""
|
165
|
+
try:
|
166
|
+
result = forward_request("pp_normalize_total", request, adinfo)
|
167
|
+
if result is not None:
|
168
|
+
return result
|
169
|
+
func_kwargs = filter_args(request, sc.pp.normalize_total)
|
170
|
+
ads = get_ads()
|
171
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
172
|
+
sc.pp.normalize_total(adata, **func_kwargs)
|
173
|
+
add_op_log(adata, sc.pp.normalize_total, func_kwargs, adinfo)
|
174
|
+
ads.set_adata(adata, adinfo=adinfo)
|
175
|
+
return [generate_msg(adinfo, adata, ads)]
|
176
|
+
except ToolError as e:
|
177
|
+
raise ToolError(e)
|
178
|
+
except Exception as e:
|
179
|
+
if hasattr(e, '__context__') and e.__context__:
|
180
|
+
raise ToolError(e.__context__)
|
181
|
+
else:
|
182
|
+
raise ToolError(e)
|
183
|
+
return _normalize_total
|
184
|
+
|
185
|
+
def _tool_highly_variable_genes(self):
|
186
|
+
def _highly_variable_genes(request: HighlyVariableGenesModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
187
|
+
"""Annotate highly variable genes"""
|
188
|
+
try:
|
189
|
+
result = forward_request("pp_highly_variable_genes", request, adinfo)
|
190
|
+
if result is not None:
|
191
|
+
return result
|
192
|
+
try:
|
193
|
+
func_kwargs = filter_args(request, sc.pp.highly_variable_genes)
|
194
|
+
ads = get_ads()
|
195
|
+
adata = ads.get_adata(adinfo=adinfo)
|
196
|
+
sc.pp.highly_variable_genes(adata, **func_kwargs)
|
197
|
+
add_op_log(adata, sc.pp.highly_variable_genes, func_kwargs, adinfo)
|
198
|
+
except Exception as e:
|
199
|
+
raise e
|
200
|
+
return [generate_msg(adinfo, adata, ads)]
|
201
|
+
except ToolError as e:
|
202
|
+
raise ToolError(e)
|
203
|
+
except Exception as e:
|
204
|
+
if hasattr(e, '__context__') and e.__context__:
|
205
|
+
raise ToolError(e.__context__)
|
206
|
+
else:
|
207
|
+
raise ToolError(e)
|
208
|
+
return _highly_variable_genes
|
209
|
+
|
210
|
+
def _tool_regress_out(self):
|
211
|
+
def _regress_out(request: RegressOutModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
212
|
+
"""Regress out (mostly) unwanted sources of variation."""
|
213
|
+
try:
|
214
|
+
result = forward_request("pp_regress_out", request, adinfo)
|
215
|
+
if result is not None:
|
216
|
+
return result
|
217
|
+
func_kwargs = filter_args(request, sc.pp.regress_out)
|
218
|
+
ads = get_ads()
|
219
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
220
|
+
sc.pp.regress_out(adata, **func_kwargs)
|
221
|
+
add_op_log(adata, sc.pp.regress_out, func_kwargs, adinfo)
|
222
|
+
ads.set_adata(adata, adinfo=adinfo)
|
223
|
+
return [generate_msg(adinfo, adata, ads)]
|
224
|
+
except ToolError as e:
|
225
|
+
raise ToolError(e)
|
226
|
+
except Exception as e:
|
227
|
+
if hasattr(e, '__context__') and e.__context__:
|
228
|
+
raise ToolError(e.__context__)
|
229
|
+
else:
|
230
|
+
raise ToolError(e)
|
231
|
+
return _regress_out
|
232
|
+
|
233
|
+
def _tool_scale(self):
|
234
|
+
def _scale(request: ScaleModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
235
|
+
"""Scale data to unit variance and zero mean"""
|
236
|
+
try:
|
237
|
+
result = forward_request("pp_scale", request, adinfo)
|
238
|
+
if result is not None:
|
239
|
+
return result
|
240
|
+
func_kwargs = filter_args(request, sc.pp.scale)
|
241
|
+
ads = get_ads()
|
242
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
243
|
+
|
244
|
+
sc.pp.scale(adata, **func_kwargs)
|
245
|
+
add_op_log(adata, sc.pp.scale, func_kwargs, adinfo)
|
246
|
+
|
247
|
+
ads.set_adata(adata, adinfo=adinfo)
|
248
|
+
return [generate_msg(adinfo, adata, ads)]
|
249
|
+
except ToolError as e:
|
250
|
+
raise ToolError(e)
|
251
|
+
except Exception as e:
|
252
|
+
if hasattr(e, '__context__') and e.__context__:
|
253
|
+
raise ToolError(e.__context__)
|
254
|
+
else:
|
255
|
+
raise ToolError(e)
|
256
|
+
return _scale
|
257
|
+
|
258
|
+
def _tool_combat(self):
|
259
|
+
def _combat(request: CombatModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
260
|
+
"""ComBat function for batch effect correction"""
|
261
|
+
try:
|
262
|
+
result = forward_request("pp_combat", request, adinfo)
|
263
|
+
if result is not None:
|
264
|
+
return result
|
265
|
+
func_kwargs = filter_args(request, sc.pp.combat)
|
266
|
+
ads = get_ads()
|
267
|
+
adata = ads.get_adata(adinfo=adinfo).copy()
|
268
|
+
|
269
|
+
sc.pp.combat(adata, **func_kwargs)
|
270
|
+
add_op_log(adata, sc.pp.combat, func_kwargs, adinfo)
|
271
|
+
|
272
|
+
ads.set_adata(adata, adinfo=adinfo)
|
273
|
+
return [generate_msg(adinfo, adata, ads)]
|
274
|
+
except ToolError as e:
|
275
|
+
raise ToolError(e)
|
276
|
+
except Exception as e:
|
277
|
+
if hasattr(e, '__context__') and e.__context__:
|
278
|
+
raise ToolError(e.__context__)
|
279
|
+
else:
|
280
|
+
raise ToolError(e)
|
281
|
+
return _combat
|
282
|
+
|
283
|
+
def _tool_scrublet(self):
|
284
|
+
def _scrublet(request: ScrubletModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
285
|
+
"""Predict doublets using Scrublet"""
|
286
|
+
try:
|
287
|
+
result = forward_request("pp_scrublet", request, adinfo)
|
288
|
+
if result is not None:
|
289
|
+
return result
|
290
|
+
func_kwargs = filter_args(request, sc.pp.scrublet)
|
291
|
+
ads = get_ads()
|
292
|
+
adata = ads.get_adata(adinfo=adinfo)
|
293
|
+
sc.pp.scrublet(adata, **func_kwargs)
|
294
|
+
add_op_log(adata, sc.pp.scrublet, func_kwargs, adinfo)
|
295
|
+
return [generate_msg(adinfo, adata, ads)]
|
296
|
+
except ToolError as e:
|
297
|
+
raise ToolError(e)
|
298
|
+
except Exception as e:
|
299
|
+
if hasattr(e, '__context__') and e.__context__:
|
300
|
+
raise ToolError(e.__context__)
|
301
|
+
else:
|
302
|
+
raise ToolError(e)
|
303
|
+
return _scrublet
|
304
|
+
|
305
|
+
def _tool_neighbors(self):
|
306
|
+
def _neighbors(request: NeighborsModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
307
|
+
"""Compute nearest neighbors distance matrix and neighborhood graph"""
|
308
|
+
try:
|
309
|
+
result = forward_request("pp_neighbors", request, adinfo)
|
310
|
+
if result is not None:
|
311
|
+
return result
|
312
|
+
func_kwargs = filter_args(request, sc.pp.neighbors)
|
313
|
+
ads = get_ads()
|
314
|
+
adata = ads.get_adata(adinfo=adinfo)
|
315
|
+
sc.pp.neighbors(adata, **func_kwargs)
|
316
|
+
add_op_log(adata, sc.pp.neighbors, func_kwargs, adinfo)
|
317
|
+
return [generate_msg(adinfo, adata, ads)]
|
318
|
+
except ToolError as e:
|
319
|
+
raise ToolError(e)
|
320
|
+
except Exception as e:
|
321
|
+
if hasattr(e, '__context__') and e.__context__:
|
322
|
+
raise ToolError(e.__context__)
|
323
|
+
else:
|
324
|
+
raise ToolError(e)
|
325
|
+
return _neighbors
|