seer-pas-sdk 0.2.1__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.
- seer_pas_sdk/core/sdk.py +428 -326
- seer_pas_sdk/core/unsupported.py +3 -3
- {seer_pas_sdk-0.2.1.dist-info → seer_pas_sdk-0.3.0.dist-info}/METADATA +2 -1
- {seer_pas_sdk-0.2.1.dist-info → seer_pas_sdk-0.3.0.dist-info}/RECORD +7 -7
- {seer_pas_sdk-0.2.1.dist-info → seer_pas_sdk-0.3.0.dist-info}/WHEEL +0 -0
- {seer_pas_sdk-0.2.1.dist-info → seer_pas_sdk-0.3.0.dist-info}/licenses/LICENSE.txt +0 -0
- {seer_pas_sdk-0.2.1.dist-info → seer_pas_sdk-0.3.0.dist-info}/top_level.txt +0 -0
seer_pas_sdk/core/sdk.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from tqdm import tqdm
|
|
2
2
|
|
|
3
|
+
import deprecation
|
|
3
4
|
import os
|
|
4
5
|
import jwt
|
|
5
6
|
import requests
|
|
@@ -70,14 +71,14 @@ class SeerSDK:
|
|
|
70
71
|
|
|
71
72
|
return sess
|
|
72
73
|
|
|
73
|
-
def
|
|
74
|
+
def get_user_tenant(self, index=True):
|
|
74
75
|
"""
|
|
75
76
|
Fetches the tenant metadata for the authenticated user.
|
|
76
77
|
|
|
77
78
|
Returns
|
|
78
79
|
-------
|
|
79
|
-
response : dict
|
|
80
|
-
A
|
|
80
|
+
response : list[dict]
|
|
81
|
+
A list of tenant objects pertaining to the user.
|
|
81
82
|
"""
|
|
82
83
|
with self._get_auth_session() as s:
|
|
83
84
|
response = s.get(f"{self._auth.url}api/v1/usertenants")
|
|
@@ -89,7 +90,13 @@ class SeerSDK:
|
|
|
89
90
|
|
|
90
91
|
response = response.json()
|
|
91
92
|
if index:
|
|
92
|
-
|
|
93
|
+
mapper = dict()
|
|
94
|
+
for x in response:
|
|
95
|
+
if x["institution"] not in mapper:
|
|
96
|
+
mapper[x["institution"]] = [x]
|
|
97
|
+
else:
|
|
98
|
+
mapper[x["institution"]].append(x)
|
|
99
|
+
return mapper
|
|
93
100
|
else:
|
|
94
101
|
return response
|
|
95
102
|
|
|
@@ -104,10 +111,10 @@ class SeerSDK:
|
|
|
104
111
|
|
|
105
112
|
Returns
|
|
106
113
|
-------
|
|
107
|
-
tenants : dict
|
|
114
|
+
tenants : dict[str, str]
|
|
108
115
|
A dictionary containing the institution names and tenant ids for the authenticated user.
|
|
109
116
|
"""
|
|
110
|
-
tenants = self.
|
|
117
|
+
tenants = self.get_user_tenant()
|
|
111
118
|
if reverse:
|
|
112
119
|
return {x["tenantId"]: x["institution"] for x in tenants.values()}
|
|
113
120
|
else:
|
|
@@ -127,13 +134,15 @@ class SeerSDK:
|
|
|
127
134
|
tenant_id: str
|
|
128
135
|
Returns the value of the active tenant id after the operation.
|
|
129
136
|
"""
|
|
130
|
-
map = self.
|
|
131
|
-
|
|
137
|
+
map = self.get_user_tenant()
|
|
138
|
+
tenant_id_match = [
|
|
139
|
+
y for x in map.values() for y in x if y["tenantId"] == identifier
|
|
140
|
+
]
|
|
132
141
|
institution_names = map.keys()
|
|
133
142
|
|
|
134
|
-
if
|
|
143
|
+
if tenant_id_match:
|
|
135
144
|
tenant_id = identifier
|
|
136
|
-
row =
|
|
145
|
+
row = tenant_id_match
|
|
137
146
|
if row:
|
|
138
147
|
row = row[0]
|
|
139
148
|
else:
|
|
@@ -141,7 +150,12 @@ class SeerSDK:
|
|
|
141
150
|
"Invalid tenant identifier. Tenant was not switched."
|
|
142
151
|
)
|
|
143
152
|
elif identifier in institution_names:
|
|
144
|
-
|
|
153
|
+
results = map[identifier]
|
|
154
|
+
if len(results) > 1:
|
|
155
|
+
raise ValueError(
|
|
156
|
+
"Multiple tenants found for the given institution name. Please specify a tenant ID."
|
|
157
|
+
)
|
|
158
|
+
row = results[0]
|
|
145
159
|
tenant_id = row["tenantId"]
|
|
146
160
|
else:
|
|
147
161
|
raise ValueError(
|
|
@@ -172,10 +186,10 @@ class SeerSDK:
|
|
|
172
186
|
|
|
173
187
|
Returns
|
|
174
188
|
-------
|
|
175
|
-
tenant: dict
|
|
189
|
+
tenant: dict[str, str]
|
|
176
190
|
Tenant metadata for the authenticated user containing "institution" and "tenantId" keys.
|
|
177
191
|
"""
|
|
178
|
-
tenants = self.
|
|
192
|
+
tenants = self.get_user_tenant(index=False)
|
|
179
193
|
row = [
|
|
180
194
|
x for x in tenants if x["tenantId"] == self._auth.active_tenant_id
|
|
181
195
|
]
|
|
@@ -211,7 +225,7 @@ class SeerSDK:
|
|
|
211
225
|
|
|
212
226
|
Returns
|
|
213
227
|
-------
|
|
214
|
-
spaces: list
|
|
228
|
+
spaces: list[dict]
|
|
215
229
|
List of space objects for the authenticated user.
|
|
216
230
|
|
|
217
231
|
Examples
|
|
@@ -237,7 +251,7 @@ class SeerSDK:
|
|
|
237
251
|
)
|
|
238
252
|
return spaces.json()
|
|
239
253
|
|
|
240
|
-
def
|
|
254
|
+
def get_plates(self, plate_id: str = None, as_df: bool = False):
|
|
241
255
|
"""
|
|
242
256
|
Fetches a list of plates for the authenticated user. If no `plate_id` is provided, returns all plates for the authenticated user. If `plate_id` is provided, returns the plate with the given `plate_id`, provided it exists.
|
|
243
257
|
|
|
@@ -245,25 +259,25 @@ class SeerSDK:
|
|
|
245
259
|
----------
|
|
246
260
|
plate_id : str, optional
|
|
247
261
|
ID of the plate to be fetched, defaulted to None.
|
|
248
|
-
|
|
249
|
-
|
|
262
|
+
as_df: bool
|
|
263
|
+
whether the result should be converted to a DataFrame, defaulted to None.
|
|
250
264
|
|
|
251
265
|
Returns
|
|
252
266
|
-------
|
|
253
|
-
plates: list or DataFrame
|
|
267
|
+
plates: list[dict] or DataFrame
|
|
254
268
|
List/DataFrame of plate objects for the authenticated user.
|
|
255
269
|
|
|
256
270
|
Examples
|
|
257
271
|
-------
|
|
258
272
|
>>> from seer_pas_sdk import SeerSDK
|
|
259
273
|
>>> seer_sdk = SeerSDK()
|
|
260
|
-
>>> seer_sdk.
|
|
274
|
+
>>> seer_sdk.get_plates()
|
|
261
275
|
>>> [
|
|
262
276
|
{ "id": ... },
|
|
263
277
|
{ "id": ... },
|
|
264
278
|
...
|
|
265
279
|
]
|
|
266
|
-
>>> seer_sdk.
|
|
280
|
+
>>> seer_sdk.get_plates(as_df=True)
|
|
267
281
|
>>> id ... user_group
|
|
268
282
|
0 a7c12190-15da-11ee-bdf1-bbaa73585acf ... None
|
|
269
283
|
1 8c3b1480-15da-11ee-bdf1-bbaa73585acf ... None
|
|
@@ -302,9 +316,9 @@ class SeerSDK:
|
|
|
302
316
|
for entry in res:
|
|
303
317
|
del entry["tenant_id"]
|
|
304
318
|
|
|
305
|
-
return res if not
|
|
319
|
+
return res if not as_df else dict_to_df(res)
|
|
306
320
|
|
|
307
|
-
def
|
|
321
|
+
def get_projects(self, project_id: str = None, as_df: bool = False):
|
|
308
322
|
"""
|
|
309
323
|
Fetches a list of projects for the authenticated user. If no `project_id` is provided, returns all projects for the authenticated user. If `project_id` is provided, returns the project with the given `project_id`, provided it exists.
|
|
310
324
|
|
|
@@ -312,26 +326,26 @@ class SeerSDK:
|
|
|
312
326
|
----------
|
|
313
327
|
project_id: str, optional
|
|
314
328
|
Project ID of the project to be fetched, defaulted to None.
|
|
315
|
-
|
|
316
|
-
|
|
329
|
+
as_df: bool
|
|
330
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
317
331
|
|
|
318
332
|
Returns
|
|
319
333
|
-------
|
|
320
|
-
projects: list or DataFrame
|
|
334
|
+
projects: list[dict] or DataFrame
|
|
321
335
|
DataFrame or list of project objects for the authenticated user.
|
|
322
336
|
|
|
323
337
|
Examples
|
|
324
338
|
-------
|
|
325
339
|
>>> from seer_pas_sdk import SeerSDK
|
|
326
340
|
>>> seer_sdk = SeerSDK()
|
|
327
|
-
>>> seer_sdk.
|
|
341
|
+
>>> seer_sdk.get_projects()
|
|
328
342
|
>>> [
|
|
329
343
|
{ "project_name": ... },
|
|
330
344
|
{ "project_name": ... },
|
|
331
345
|
...
|
|
332
346
|
]
|
|
333
347
|
|
|
334
|
-
>>> seer_sdk.
|
|
348
|
+
>>> seer_sdk.get_projects(as_df=True)
|
|
335
349
|
>>> id ... user_group
|
|
336
350
|
0 a7c12190-15da-11ee-bdf1-bbaa73585acf ... None
|
|
337
351
|
1 8c3b1480-15da-11ee-bdf1-bbaa73585acf ... None
|
|
@@ -345,7 +359,7 @@ class SeerSDK:
|
|
|
345
359
|
938 5b05d440-6610-11ea-96e3-d5a4dab4ebf6 ... None
|
|
346
360
|
939 9872e3f0-544e-11ea-ad9e-1991e0725494 ... None
|
|
347
361
|
|
|
348
|
-
>>> seer_sdk.
|
|
362
|
+
>>> seer_sdk.get_projects(id="YOUR_PROJECT_ID_HERE")
|
|
349
363
|
>>> [{ "project_name": ... }]
|
|
350
364
|
"""
|
|
351
365
|
|
|
@@ -379,15 +393,18 @@ class SeerSDK:
|
|
|
379
393
|
entry["raw_file_path"] = entry["raw_file_path"][
|
|
380
394
|
location(entry["raw_file_path"]) :
|
|
381
395
|
]
|
|
382
|
-
return res if not
|
|
396
|
+
return res if not as_df else dict_to_df(res)
|
|
383
397
|
|
|
384
|
-
def
|
|
385
|
-
self,
|
|
398
|
+
def get_samples(
|
|
399
|
+
self,
|
|
400
|
+
plate_id: str = None,
|
|
401
|
+
project_id: str = None,
|
|
402
|
+
analysis_id: str = None,
|
|
403
|
+
analysis_name: str = None,
|
|
404
|
+
as_df: bool = False,
|
|
386
405
|
):
|
|
387
406
|
"""
|
|
388
|
-
Fetches a list of samples for the authenticated user,
|
|
389
|
-
|
|
390
|
-
If both `plate_id` and `project_id` are passed in, only the `plate_id` is validated first.
|
|
407
|
+
Fetches a list of samples for the authenticated user with relation to a specified plate, project, or analysis. If no parameters are provided, returns all samples for the authenticated user. If `plate_id` or `project_id` is provided, returns samples associated with that plate or project. If `analysis_id` or `analysis_name` is provided, returns samples associated with that analysis.
|
|
391
408
|
|
|
392
409
|
Parameters
|
|
393
410
|
----------
|
|
@@ -395,12 +412,16 @@ class SeerSDK:
|
|
|
395
412
|
ID of the plate for which samples are to be fetched, defaulted to None.
|
|
396
413
|
project_id : str, optional
|
|
397
414
|
ID of the project for which samples are to be fetched, defaulted to None.
|
|
398
|
-
|
|
399
|
-
|
|
415
|
+
analysis_id : str, optional
|
|
416
|
+
ID of the analysis for which samples are to be fetched, defaulted to None.
|
|
417
|
+
analysis_name : str, optional
|
|
418
|
+
Name of the analysis for which samples are to be fetched, defaulted to None.
|
|
419
|
+
as_df: bool
|
|
420
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
400
421
|
|
|
401
422
|
Returns
|
|
402
423
|
-------
|
|
403
|
-
samples: list or DataFrame
|
|
424
|
+
samples: list[dict] or DataFrame
|
|
404
425
|
List/DataFrame of samples for the authenticated user.
|
|
405
426
|
|
|
406
427
|
Examples
|
|
@@ -408,14 +429,14 @@ class SeerSDK:
|
|
|
408
429
|
>>> from seer_pas_sdk import SeerSDK
|
|
409
430
|
>>> seer_sdk = SeerSDK()
|
|
410
431
|
|
|
411
|
-
>>> seer_sdk.
|
|
432
|
+
>>> seer_sdk.get_samples(plate_id="7ec8cad0-15e0-11ee-bdf1-bbaa73585acf")
|
|
412
433
|
>>> [
|
|
413
434
|
{ "id": ... },
|
|
414
435
|
{ "id": ... },
|
|
415
436
|
...
|
|
416
437
|
]
|
|
417
438
|
|
|
418
|
-
>>> seer_sdk.
|
|
439
|
+
>>> seer_sdk.get_samples(as_df=True)
|
|
419
440
|
>>> id ... control
|
|
420
441
|
0 812139c0-15e0-11ee-bdf1-bbaa73585acf ...
|
|
421
442
|
1 803e05b0-15e0-11ee-bdf1-bbaa73585acf ... MPE Control
|
|
@@ -430,29 +451,40 @@ class SeerSDK:
|
|
|
430
451
|
3628 dd607ef0-654c-11ea-8eb2-25a1cfd1163c ... C132
|
|
431
452
|
"""
|
|
432
453
|
|
|
433
|
-
if
|
|
434
|
-
|
|
454
|
+
# Raise an error if none or more than one of the primary key parameters are passed in.
|
|
455
|
+
if (
|
|
456
|
+
sum(
|
|
457
|
+
[
|
|
458
|
+
True if x else False
|
|
459
|
+
for x in [plate_id, project_id, analysis_id, analysis_name]
|
|
460
|
+
]
|
|
461
|
+
)
|
|
462
|
+
!= 1
|
|
463
|
+
):
|
|
464
|
+
raise ValueError(
|
|
465
|
+
"You must pass in exactly one of plate_id, project_id, analysis_id, analysis_name."
|
|
466
|
+
)
|
|
435
467
|
|
|
436
468
|
res = []
|
|
437
469
|
URL = f"{self._auth.url}api/v1/samples"
|
|
438
470
|
sample_params = {"all": "true"}
|
|
439
471
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
472
|
+
if project_id or plate_id:
|
|
473
|
+
with self._get_auth_session() as s:
|
|
474
|
+
if plate_id:
|
|
475
|
+
try:
|
|
476
|
+
self.get_plates(plate_id)
|
|
477
|
+
except:
|
|
478
|
+
raise ValueError("Plate ID is invalid.")
|
|
479
|
+
sample_params["plateId"] = plate_id
|
|
448
480
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
481
|
+
else:
|
|
482
|
+
try:
|
|
483
|
+
self.get_projects(project_id)
|
|
484
|
+
except:
|
|
485
|
+
raise ValueError("Project ID is invalid.")
|
|
454
486
|
|
|
455
|
-
|
|
487
|
+
sample_params["projectId"] = project_id
|
|
456
488
|
|
|
457
489
|
samples = s.get(URL, params=sample_params)
|
|
458
490
|
if samples.status_code != 200:
|
|
@@ -460,14 +492,27 @@ class SeerSDK:
|
|
|
460
492
|
f"Failed to fetch sample data for plate ID: {plate_id}."
|
|
461
493
|
)
|
|
462
494
|
res = samples.json()["data"]
|
|
495
|
+
res_df = dict_to_df(res)
|
|
463
496
|
|
|
464
|
-
for
|
|
465
|
-
|
|
497
|
+
# API returns empty strings if not a control, replace with None for filtering purposes
|
|
498
|
+
res_df["control"] = res_df["control"].apply(
|
|
499
|
+
lambda x: x if x else None
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
if analysis_id:
|
|
503
|
+
res_df = self._get_analysis_samples(
|
|
504
|
+
analysis_id=analysis_id, as_df=True
|
|
505
|
+
)
|
|
506
|
+
else:
|
|
507
|
+
res_df = self._get_analysis_samples(
|
|
508
|
+
analysis_name=analysis_name, as_df=True, is_name=True
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# apply post processing
|
|
512
|
+
res_df.drop(["tenant_id"], axis=1, inplace=True)
|
|
466
513
|
|
|
467
|
-
# Exclude custom fields that don't belong to the tenant
|
|
468
|
-
res_df = dict_to_df(res)
|
|
469
514
|
custom_columns = [
|
|
470
|
-
x["field_name"] for x in self.
|
|
515
|
+
x["field_name"] for x in self._get_sample_custom_fields()
|
|
471
516
|
]
|
|
472
517
|
res_df = res_df[
|
|
473
518
|
[
|
|
@@ -477,10 +522,7 @@ class SeerSDK:
|
|
|
477
522
|
]
|
|
478
523
|
]
|
|
479
524
|
|
|
480
|
-
|
|
481
|
-
res_df["control"] = res_df["control"].apply(lambda x: x if x else None)
|
|
482
|
-
|
|
483
|
-
return res_df.to_dict(orient="records") if not df else res_df
|
|
525
|
+
return res_df.to_dict(orient="records") if not as_df else res_df
|
|
484
526
|
|
|
485
527
|
def _filter_samples_metadata(
|
|
486
528
|
self,
|
|
@@ -505,7 +547,7 @@ class SeerSDK:
|
|
|
505
547
|
|
|
506
548
|
Returns
|
|
507
549
|
-------
|
|
508
|
-
res : list
|
|
550
|
+
res : list[str]
|
|
509
551
|
A list of sample ids
|
|
510
552
|
|
|
511
553
|
Examples
|
|
@@ -533,7 +575,7 @@ class SeerSDK:
|
|
|
533
575
|
"Invalid filter. Please choose between 'control' or 'sample'."
|
|
534
576
|
)
|
|
535
577
|
|
|
536
|
-
df = self.
|
|
578
|
+
df = self.get_samples(project_id=project_id, as_df=True)
|
|
537
579
|
|
|
538
580
|
if filter == "control":
|
|
539
581
|
df = df[~df["control"].isna()]
|
|
@@ -546,7 +588,7 @@ class SeerSDK:
|
|
|
546
588
|
|
|
547
589
|
return valid_samples
|
|
548
590
|
|
|
549
|
-
def
|
|
591
|
+
def _get_sample_custom_fields(self):
|
|
550
592
|
"""
|
|
551
593
|
Fetches a list of custom fields defined for the authenticated user.
|
|
552
594
|
"""
|
|
@@ -566,7 +608,7 @@ class SeerSDK:
|
|
|
566
608
|
del entry["tenant_id"]
|
|
567
609
|
return res
|
|
568
610
|
|
|
569
|
-
def
|
|
611
|
+
def get_msruns(self, sample_ids: list, as_df: bool = False):
|
|
570
612
|
"""
|
|
571
613
|
Fetches MS data files for passed in `sample_ids` (provided they are valid and contain relevant files) for an authenticated user.
|
|
572
614
|
|
|
@@ -576,12 +618,12 @@ class SeerSDK:
|
|
|
576
618
|
----------
|
|
577
619
|
sample_ids : list
|
|
578
620
|
List of unique sample IDs.
|
|
579
|
-
|
|
580
|
-
|
|
621
|
+
as_df: bool
|
|
622
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
581
623
|
|
|
582
624
|
Returns
|
|
583
625
|
-------
|
|
584
|
-
res: list or DataFrame
|
|
626
|
+
res: list[dict] or DataFrame
|
|
585
627
|
List/DataFrame of plate objects for the authenticated user.
|
|
586
628
|
|
|
587
629
|
Examples
|
|
@@ -590,13 +632,13 @@ class SeerSDK:
|
|
|
590
632
|
>>> seer_sdk = SeerSDK()
|
|
591
633
|
>>> sample_ids = ["812139c0-15e0-11ee-bdf1-bbaa73585acf", "803e05b0-15e0-11ee-bdf1-bbaa73585acf"]
|
|
592
634
|
|
|
593
|
-
>>> seer_sdk.
|
|
635
|
+
>>> seer_sdk.get_msruns(sample_ids)
|
|
594
636
|
>>> [
|
|
595
637
|
{"id": "SAMPLE_ID_1_HERE" ... },
|
|
596
638
|
{"id": "SAMPLE_ID_2_HERE" ... }
|
|
597
639
|
]
|
|
598
640
|
|
|
599
|
-
>>> seer_sdk.
|
|
641
|
+
>>> seer_sdk.get_msruns(sample_ids, as_df=True)
|
|
600
642
|
>>> id ... gradient
|
|
601
643
|
0 81c6a180-15e0-11ee-bdf1-bbaa73585acf ... None
|
|
602
644
|
1 816a9ed0-15e0-11ee-bdf1-bbaa73585acf ... None
|
|
@@ -631,209 +673,7 @@ class SeerSDK:
|
|
|
631
673
|
entry["raw_file_path"] = entry["raw_file_path"][
|
|
632
674
|
location(entry["raw_file_path"]) :
|
|
633
675
|
]
|
|
634
|
-
return res if not
|
|
635
|
-
|
|
636
|
-
def get_plate(self, plate_id: str, df: bool = False):
|
|
637
|
-
"""
|
|
638
|
-
Fetches MS data files for a `plate_id` (provided that the `plate_id` is valid and has samples associated with it) for an authenticated user.
|
|
639
|
-
|
|
640
|
-
The function returns a dict containing DataFrame objects if the `df` flag is passed in as True, otherwise a nested dict object is returned instead.
|
|
641
|
-
|
|
642
|
-
Parameters
|
|
643
|
-
----------
|
|
644
|
-
plate_id : str, optional
|
|
645
|
-
ID of the plate for which samples are to be fetched, defaulted to None.
|
|
646
|
-
df: bool
|
|
647
|
-
Boolean denoting whether the user wants the response back in JSON or a DataFrame object
|
|
648
|
-
|
|
649
|
-
Returns
|
|
650
|
-
-------
|
|
651
|
-
res: list or DataFrame
|
|
652
|
-
List/DataFrame of MS data file objects for the authenticated user.
|
|
653
|
-
|
|
654
|
-
Examples
|
|
655
|
-
-------
|
|
656
|
-
>>> from seer_pas_sdk import SeerSDK
|
|
657
|
-
>>> seer_sdk = SeerSDK()
|
|
658
|
-
>>> plate_id = "7ec8cad0-15e0-11ee-bdf1-bbaa73585acf"
|
|
659
|
-
|
|
660
|
-
>>> seer_sdk.get_plate(plate_id)
|
|
661
|
-
>>> [
|
|
662
|
-
{"id": "PLATE_ID_1_HERE" ... },
|
|
663
|
-
{"id": "PLATE_ID_2_HERE" ... }
|
|
664
|
-
]
|
|
665
|
-
|
|
666
|
-
>>> seer_sdk.get_plate(plate_id, df=True)
|
|
667
|
-
>>> id ... volume
|
|
668
|
-
0 PLATE_ID_1_HERE ... None
|
|
669
|
-
1 PLATE_ID_2_HERE ... None
|
|
670
|
-
|
|
671
|
-
[2 rows x 26 columns]
|
|
672
|
-
"""
|
|
673
|
-
plate_samples = self.get_samples_metadata(plate_id=plate_id)
|
|
674
|
-
sample_ids = [sample["id"] for sample in plate_samples]
|
|
675
|
-
return self.get_msdata(sample_ids, df)
|
|
676
|
-
|
|
677
|
-
def get_project(
|
|
678
|
-
self,
|
|
679
|
-
project_id: str,
|
|
680
|
-
msdata: bool = False,
|
|
681
|
-
df: bool = False,
|
|
682
|
-
flat: bool = False,
|
|
683
|
-
):
|
|
684
|
-
"""
|
|
685
|
-
Fetches samples (and MS data files) for a `project_id` (provided that the `project_id` is valid and has samples associated with it) for an authenticated user.
|
|
686
|
-
|
|
687
|
-
The function returns a DataFrame object if the `df` flag is passed in as True, otherwise a nested dict object is returned instead. If the both the `df` and `msdata` flags are passed in as True, then a nested DataFrame object is returned instead.
|
|
688
|
-
|
|
689
|
-
If the `flat` flag is passed in as True, then the nested dict object is returned as an array of dict objects and the nested df object is returned as a single df object.
|
|
690
|
-
|
|
691
|
-
Parameters
|
|
692
|
-
----------
|
|
693
|
-
project_id : str
|
|
694
|
-
ID of the project for which samples are to be fetched.
|
|
695
|
-
msdata: bool, optional
|
|
696
|
-
Boolean flag denoting whether the user wants relevant MS data files associated with the samples.
|
|
697
|
-
df: bool, optional
|
|
698
|
-
Boolean denoting whether the user wants the response back in JSON or a DataFrame object.
|
|
699
|
-
|
|
700
|
-
Returns
|
|
701
|
-
-------
|
|
702
|
-
res: list or DataFrame
|
|
703
|
-
List/DataFrame of plate objects for the authenticated user.
|
|
704
|
-
|
|
705
|
-
Examples
|
|
706
|
-
-------
|
|
707
|
-
>>> from seer_pas_sdk import SeerSDK
|
|
708
|
-
>>> seer_sdk = SeerSDK()
|
|
709
|
-
>>> project_id = "7e48e150-8a47-11ed-b382-bf440acece26"
|
|
710
|
-
|
|
711
|
-
>>> seer_sdk.get_project(project_id=project_id, msdata=False, df=False)
|
|
712
|
-
>>> {
|
|
713
|
-
"project_samples": [
|
|
714
|
-
{
|
|
715
|
-
"id": "SAMPLE_ID_1_HERE",
|
|
716
|
-
"sample_type": "Plasma",
|
|
717
|
-
...
|
|
718
|
-
...
|
|
719
|
-
},
|
|
720
|
-
{
|
|
721
|
-
"id": "SAMPLE_ID_2_HERE",
|
|
722
|
-
"sample_type": "Plasma",
|
|
723
|
-
...
|
|
724
|
-
...
|
|
725
|
-
}
|
|
726
|
-
]
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
>>> seer_sdk.get_project(project_id=project_id, msdata=True, df=False)
|
|
730
|
-
>>> [
|
|
731
|
-
{
|
|
732
|
-
"id": "SAMPLE_ID_1_HERE",
|
|
733
|
-
"sample_type": "Plasma",
|
|
734
|
-
...
|
|
735
|
-
...
|
|
736
|
-
"ms_data_files": [
|
|
737
|
-
{
|
|
738
|
-
"id": MS_DATA_FILE_ID_1_HERE,
|
|
739
|
-
"tenant_id": "TENANT_ID_HERE",
|
|
740
|
-
...
|
|
741
|
-
...
|
|
742
|
-
},
|
|
743
|
-
{
|
|
744
|
-
"id": MS_DATA_FILE_ID_1_HERE,
|
|
745
|
-
"tenant_id": "TENANT_ID_HERE",
|
|
746
|
-
...
|
|
747
|
-
...
|
|
748
|
-
}
|
|
749
|
-
]
|
|
750
|
-
},
|
|
751
|
-
{
|
|
752
|
-
"id": "SAMPLE_ID_2_HERE",
|
|
753
|
-
"sample_type": "Plasma",
|
|
754
|
-
...
|
|
755
|
-
...
|
|
756
|
-
"ms_data_files": [
|
|
757
|
-
{
|
|
758
|
-
"id": MS_DATA_FILE_ID_2_HERE,
|
|
759
|
-
"tenant_id": "TENANT_ID_HERE",
|
|
760
|
-
...
|
|
761
|
-
...
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
"id": MS_DATA_FILE_ID_2_HERE,
|
|
765
|
-
"tenant_id": "TENANT_ID_HERE",
|
|
766
|
-
...
|
|
767
|
-
...
|
|
768
|
-
}
|
|
769
|
-
]
|
|
770
|
-
}
|
|
771
|
-
]
|
|
772
|
-
|
|
773
|
-
>>> seer_sdk.get_project(project_id=project_id, msdata=True, df=True)
|
|
774
|
-
>>> id ... ms_data_files
|
|
775
|
-
0 829509f0-8a47-11ed-b382-bf440acece26 ... id ... g...
|
|
776
|
-
1 828d41c0-8a47-11ed-b382-bf440acece26 ... id ... g...
|
|
777
|
-
2 8294e2e0-8a47-11ed-b382-bf440acece26 ... id ... g...
|
|
778
|
-
3 8285eec0-8a47-11ed-b382-bf440acece26 ... id ... g...
|
|
779
|
-
|
|
780
|
-
[4 rows x 60 columns]
|
|
781
|
-
"""
|
|
782
|
-
if not project_id:
|
|
783
|
-
return ValueError("No project ID specified.")
|
|
784
|
-
|
|
785
|
-
sample_ids = []
|
|
786
|
-
project_samples = self.get_samples_metadata(
|
|
787
|
-
project_id=project_id, df=False
|
|
788
|
-
)
|
|
789
|
-
flat_result = []
|
|
790
|
-
|
|
791
|
-
if msdata:
|
|
792
|
-
|
|
793
|
-
# construct map for quick index reference of sample in project_samples
|
|
794
|
-
sample_ids = {
|
|
795
|
-
sample["id"]: i for i, sample in enumerate(project_samples)
|
|
796
|
-
} # will always contain unique values
|
|
797
|
-
ms_data_files = self.get_msdata(
|
|
798
|
-
sample_ids=list(sample_ids.keys()), df=False
|
|
799
|
-
)
|
|
800
|
-
|
|
801
|
-
for ms_data_file in ms_data_files:
|
|
802
|
-
index = sample_ids.get(ms_data_file["sample_id"], None)
|
|
803
|
-
if not index:
|
|
804
|
-
continue
|
|
805
|
-
|
|
806
|
-
if not flat:
|
|
807
|
-
if "ms_data_file" not in project_samples[index]:
|
|
808
|
-
project_samples[index]["ms_data_files"] = [
|
|
809
|
-
ms_data_file
|
|
810
|
-
]
|
|
811
|
-
else:
|
|
812
|
-
project_samples[index]["ms_data_files"].append(
|
|
813
|
-
ms_data_file
|
|
814
|
-
)
|
|
815
|
-
else:
|
|
816
|
-
flat_result.append(project_samples[index] | ms_data_file)
|
|
817
|
-
|
|
818
|
-
# return flat result if results were added to the flat object
|
|
819
|
-
if flat and flat_result:
|
|
820
|
-
project_samples = flat_result
|
|
821
|
-
|
|
822
|
-
if df:
|
|
823
|
-
if flat:
|
|
824
|
-
return pd.DataFrame(project_samples)
|
|
825
|
-
else:
|
|
826
|
-
for sample_index in range(len(project_samples)):
|
|
827
|
-
if "ms_data_files" in project_samples[sample_index]:
|
|
828
|
-
project_samples[sample_index]["ms_data_files"] = (
|
|
829
|
-
dict_to_df(
|
|
830
|
-
project_samples[sample_index]["ms_data_files"]
|
|
831
|
-
)
|
|
832
|
-
)
|
|
833
|
-
|
|
834
|
-
project_samples = dict_to_df(project_samples)
|
|
835
|
-
|
|
836
|
-
return project_samples
|
|
676
|
+
return res if not as_df else dict_to_df(res)
|
|
837
677
|
|
|
838
678
|
def get_analysis_protocols(
|
|
839
679
|
self,
|
|
@@ -853,7 +693,7 @@ class SeerSDK:
|
|
|
853
693
|
|
|
854
694
|
Returns
|
|
855
695
|
-------
|
|
856
|
-
protocols: list
|
|
696
|
+
protocols: list[dict]
|
|
857
697
|
List of analysis protocol objects for the authenticated user.
|
|
858
698
|
|
|
859
699
|
Examples
|
|
@@ -920,7 +760,7 @@ class SeerSDK:
|
|
|
920
760
|
|
|
921
761
|
return res
|
|
922
762
|
|
|
923
|
-
def
|
|
763
|
+
def get_analyses(
|
|
924
764
|
self,
|
|
925
765
|
analysis_id: str = None,
|
|
926
766
|
folder_id: str = None,
|
|
@@ -962,30 +802,30 @@ class SeerSDK:
|
|
|
962
802
|
|
|
963
803
|
Returns
|
|
964
804
|
-------
|
|
965
|
-
analyses: dict
|
|
805
|
+
analyses: list[dict]
|
|
966
806
|
Contains a list of analyses objects for the authenticated user.
|
|
967
807
|
|
|
968
808
|
Examples
|
|
969
809
|
-------
|
|
970
810
|
>>> from seer_pas_sdk import SeerSDK
|
|
971
811
|
>>> seer_sdk = SeerSDK()
|
|
972
|
-
>>> seer_sdk.
|
|
812
|
+
>>> seer_sdk.get_analyses()
|
|
973
813
|
>>> [
|
|
974
814
|
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
975
815
|
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
976
816
|
{id: "YOUR_ANALYSIS_ID_HERE", ...}
|
|
977
817
|
]
|
|
978
818
|
|
|
979
|
-
>>> seer_sdk.
|
|
819
|
+
>>> seer_sdk.get_analyses("YOUR_ANALYSIS_ID_HERE")
|
|
980
820
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
981
821
|
|
|
982
|
-
>>> seer_sdk.
|
|
822
|
+
>>> seer_sdk.get_analyses(folder_name="YOUR_FOLDER_NAME_HERE")
|
|
983
823
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
984
824
|
|
|
985
|
-
>>> seer_sdk.
|
|
825
|
+
>>> seer_sdk.get_analyses(analysis_name="YOUR_ANALYSIS")
|
|
986
826
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
987
827
|
|
|
988
|
-
>>> seer_sdk.
|
|
828
|
+
>>> seer_sdk.get_analyses(description="YOUR_DESCRIPTION")
|
|
989
829
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
990
830
|
"""
|
|
991
831
|
|
|
@@ -1074,7 +914,7 @@ class SeerSDK:
|
|
|
1074
914
|
|
|
1075
915
|
# recursive solution to get analyses in folders
|
|
1076
916
|
for folder in folders:
|
|
1077
|
-
res += self.
|
|
917
|
+
res += self.get_analyses(folder_id=folder)
|
|
1078
918
|
|
|
1079
919
|
if analysis_only:
|
|
1080
920
|
res = [
|
|
@@ -1082,6 +922,7 @@ class SeerSDK:
|
|
|
1082
922
|
]
|
|
1083
923
|
return res
|
|
1084
924
|
|
|
925
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="1.0.0")
|
|
1085
926
|
def get_analysis_result_protein_data(
|
|
1086
927
|
self, analysis_id: str, link: bool = False, pg: str = None
|
|
1087
928
|
):
|
|
@@ -1154,6 +995,7 @@ class SeerSDK:
|
|
|
1154
995
|
"protein_panel": protein_panel,
|
|
1155
996
|
}
|
|
1156
997
|
|
|
998
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="1.0.0")
|
|
1157
999
|
def get_analysis_result_peptide_data(
|
|
1158
1000
|
self, analysis_id: str, link: bool = False, peptide: str = None
|
|
1159
1001
|
):
|
|
@@ -1229,7 +1071,92 @@ class SeerSDK:
|
|
|
1229
1071
|
"peptide_panel": peptide_panel,
|
|
1230
1072
|
}
|
|
1231
1073
|
|
|
1232
|
-
def
|
|
1074
|
+
def _get_search_result_protein_data(self, analysis_id: str):
|
|
1075
|
+
"""
|
|
1076
|
+
Given an analysis id, this function returns the protein data for the analysis.
|
|
1077
|
+
|
|
1078
|
+
Parameters
|
|
1079
|
+
----------
|
|
1080
|
+
analysis_id : str
|
|
1081
|
+
ID of the analysis for which the data is to be fetched.
|
|
1082
|
+
"""
|
|
1083
|
+
with self._get_auth_session() as s:
|
|
1084
|
+
URL = f"{self._auth.url}api/v1/data"
|
|
1085
|
+
response = s.get(
|
|
1086
|
+
f"{URL}/protein?analysisId={analysis_id}&retry=false"
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
if response.status_code != 200:
|
|
1090
|
+
raise ValueError(
|
|
1091
|
+
"Could not fetch protein data. Please verify that your analysis completed."
|
|
1092
|
+
)
|
|
1093
|
+
response = response.json()
|
|
1094
|
+
|
|
1095
|
+
protein_data = {}
|
|
1096
|
+
for row in response:
|
|
1097
|
+
if row.get("name") == "npLink":
|
|
1098
|
+
protein_data["npLink"] = {
|
|
1099
|
+
"url": row.get("link", {}).get("url", "")
|
|
1100
|
+
}
|
|
1101
|
+
if row.get("name") == "panelLink":
|
|
1102
|
+
protein_data["panelLink"] = {
|
|
1103
|
+
"url": row.get("link", {}).get("url", "")
|
|
1104
|
+
}
|
|
1105
|
+
if not protein_data:
|
|
1106
|
+
raise ValueError("No protein result files found.")
|
|
1107
|
+
if not "panelLink" in protein_data.keys():
|
|
1108
|
+
protein_data["panelLink"] = {"url": ""}
|
|
1109
|
+
|
|
1110
|
+
return protein_data
|
|
1111
|
+
|
|
1112
|
+
def _get_search_result_peptide_data(self, analysis_id: str):
|
|
1113
|
+
"""
|
|
1114
|
+
Given an analysis id, this function returns the peptide data for the analysis.
|
|
1115
|
+
|
|
1116
|
+
Parameters
|
|
1117
|
+
----------
|
|
1118
|
+
|
|
1119
|
+
analysis_id : str
|
|
1120
|
+
ID of the analysis for which the data is to be fetched.
|
|
1121
|
+
|
|
1122
|
+
Returns
|
|
1123
|
+
-------
|
|
1124
|
+
peptide_data : dict[str, str]
|
|
1125
|
+
Dictionary containing URLs for npLink and panelLink peptide data.
|
|
1126
|
+
|
|
1127
|
+
"""
|
|
1128
|
+
|
|
1129
|
+
with self._get_auth_session() as s:
|
|
1130
|
+
URL = f"{self._auth.url}api/v1/data"
|
|
1131
|
+
response = s.get(
|
|
1132
|
+
f"{URL}/peptide?analysisId={analysis_id}&retry=false"
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
if response.status_code != 200:
|
|
1136
|
+
raise ValueError(
|
|
1137
|
+
"Could not fetch peptide data. Please verify that your analysis completed."
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
response = response.json()
|
|
1141
|
+
|
|
1142
|
+
peptide_data = {}
|
|
1143
|
+
for row in response:
|
|
1144
|
+
if row.get("name") == "npLink":
|
|
1145
|
+
peptide_data["npLink"] = {
|
|
1146
|
+
"url": row.get("link", {}).get("url", "")
|
|
1147
|
+
}
|
|
1148
|
+
if row.get("name") == "panelLink":
|
|
1149
|
+
peptide_data["panelLink"] = {
|
|
1150
|
+
"url": row.get("link", {}).get("url", "")
|
|
1151
|
+
}
|
|
1152
|
+
if not peptide_data:
|
|
1153
|
+
raise ValueError("No peptide result files found.")
|
|
1154
|
+
if not "panelLink" in peptide_data.keys():
|
|
1155
|
+
peptide_data["panelLink"] = {"url": ""}
|
|
1156
|
+
|
|
1157
|
+
return peptide_data
|
|
1158
|
+
|
|
1159
|
+
def list_search_result_files(self, analysis_id: str):
|
|
1233
1160
|
"""
|
|
1234
1161
|
Given an analysis id, this function returns a list of files associated with the analysis.
|
|
1235
1162
|
|
|
@@ -1240,11 +1167,11 @@ class SeerSDK:
|
|
|
1240
1167
|
|
|
1241
1168
|
Returns
|
|
1242
1169
|
-------
|
|
1243
|
-
files: list
|
|
1170
|
+
files: list[str]
|
|
1244
1171
|
List of files associated with the analysis.
|
|
1245
1172
|
"""
|
|
1246
1173
|
try:
|
|
1247
|
-
analysis_metadata = self.
|
|
1174
|
+
analysis_metadata = self.get_analyses(analysis_id)[0]
|
|
1248
1175
|
except (IndexError, ServerError):
|
|
1249
1176
|
raise ValueError("Invalid analysis ID.")
|
|
1250
1177
|
except:
|
|
@@ -1266,7 +1193,141 @@ class SeerSDK:
|
|
|
1266
1193
|
files.append(row["filename"])
|
|
1267
1194
|
return files
|
|
1268
1195
|
|
|
1269
|
-
def
|
|
1196
|
+
def get_search_result(
|
|
1197
|
+
self, analysis_id: str, analyte_type: str, rollup: str
|
|
1198
|
+
):
|
|
1199
|
+
"""
|
|
1200
|
+
Load one of the files available via the "Download result files" button on the PAS UI.
|
|
1201
|
+
|
|
1202
|
+
Args:
|
|
1203
|
+
analysis_id (str): id of the analysis
|
|
1204
|
+
analyte_type (str): type of the data. Acceptable options are one of ['protein', 'peptide', 'precursor'].
|
|
1205
|
+
rollup (str): the desired file. Acceptable options are one of ['np', 'panel'].
|
|
1206
|
+
Returns:
|
|
1207
|
+
pd.DataFrame: the requested file as a pandas DataFrame
|
|
1208
|
+
|
|
1209
|
+
"""
|
|
1210
|
+
if not analysis_id:
|
|
1211
|
+
raise ValueError("Analysis ID cannot be empty.")
|
|
1212
|
+
|
|
1213
|
+
if analyte_type not in ["protein", "peptide", "precursor"]:
|
|
1214
|
+
raise ValueError(
|
|
1215
|
+
"Invalid data type. Please choose between 'protein', 'peptide', or 'precursor'."
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
if rollup not in ["np", "panel"]:
|
|
1219
|
+
raise ValueError(
|
|
1220
|
+
"Invalid file. Please choose between 'np', 'panel'."
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
if analyte_type == "precursor" and rollup == "panel":
|
|
1224
|
+
raise ValueError(
|
|
1225
|
+
"Precursor data is not available for panel rollup, please select np rollup."
|
|
1226
|
+
)
|
|
1227
|
+
|
|
1228
|
+
if analyte_type == "protein":
|
|
1229
|
+
if rollup == "np":
|
|
1230
|
+
return url_to_df(
|
|
1231
|
+
self._get_search_result_protein_data(analysis_id)[
|
|
1232
|
+
"npLink"
|
|
1233
|
+
]["url"]
|
|
1234
|
+
)
|
|
1235
|
+
elif rollup == "panel":
|
|
1236
|
+
return url_to_df(
|
|
1237
|
+
self._get_search_result_protein_data(analysis_id)[
|
|
1238
|
+
"panelLink"
|
|
1239
|
+
]["url"]
|
|
1240
|
+
)
|
|
1241
|
+
elif analyte_type == "peptide":
|
|
1242
|
+
if rollup == "np":
|
|
1243
|
+
return url_to_df(
|
|
1244
|
+
self._get_search_result_peptide_data(analysis_id)[
|
|
1245
|
+
"npLink"
|
|
1246
|
+
]["url"]
|
|
1247
|
+
)
|
|
1248
|
+
elif rollup == "panel":
|
|
1249
|
+
return url_to_df(
|
|
1250
|
+
self._get_search_result_peptide_data(analysis_id)[
|
|
1251
|
+
"panelLink"
|
|
1252
|
+
]["url"]
|
|
1253
|
+
)
|
|
1254
|
+
else:
|
|
1255
|
+
return url_to_df(
|
|
1256
|
+
self.get_search_result_file_url(
|
|
1257
|
+
analysis_id, filename="report.tsv"
|
|
1258
|
+
)["url"]
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1261
|
+
def download_search_output_file(
|
|
1262
|
+
self, analysis_id: str, filename: str, download_path: str = ""
|
|
1263
|
+
):
|
|
1264
|
+
"""
|
|
1265
|
+
Given an analysis id and a analysis result filename, this function downloads the file to the specified path.
|
|
1266
|
+
|
|
1267
|
+
Parameters
|
|
1268
|
+
----------
|
|
1269
|
+
analysis_id : str
|
|
1270
|
+
ID of the analysis for which the data is to be fetched.
|
|
1271
|
+
|
|
1272
|
+
filename : str
|
|
1273
|
+
Name of the file to be fetched. Files can be case insensitive and without file extensions.
|
|
1274
|
+
|
|
1275
|
+
download_path : str
|
|
1276
|
+
String flag denoting where the user wants the files downloaded. Can be local or absolute as long as the path is valid.
|
|
1277
|
+
|
|
1278
|
+
Returns
|
|
1279
|
+
-------
|
|
1280
|
+
None
|
|
1281
|
+
Downloads the file to the specified path.
|
|
1282
|
+
"""
|
|
1283
|
+
|
|
1284
|
+
if not download_path:
|
|
1285
|
+
download_path = os.getcwd()
|
|
1286
|
+
|
|
1287
|
+
if not analysis_id:
|
|
1288
|
+
raise ValueError("Analysis ID cannot be empty.")
|
|
1289
|
+
|
|
1290
|
+
if not os.path.exists(download_path):
|
|
1291
|
+
raise ValueError(
|
|
1292
|
+
"Please specify a valid folder path as download path."
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
file = self.get_search_result_file_url(analysis_id, filename)
|
|
1296
|
+
file_url = file["url"]
|
|
1297
|
+
filename = file["filename"]
|
|
1298
|
+
|
|
1299
|
+
print("Downloading file:", filename)
|
|
1300
|
+
for _ in range(2):
|
|
1301
|
+
try:
|
|
1302
|
+
with tqdm(
|
|
1303
|
+
unit="B",
|
|
1304
|
+
unit_scale=True,
|
|
1305
|
+
unit_divisor=1024,
|
|
1306
|
+
miniters=1,
|
|
1307
|
+
desc=f"Progress",
|
|
1308
|
+
) as t:
|
|
1309
|
+
ssl._create_default_https_context = (
|
|
1310
|
+
ssl._create_unverified_context
|
|
1311
|
+
)
|
|
1312
|
+
urllib.request.urlretrieve(
|
|
1313
|
+
file_url,
|
|
1314
|
+
f"{download_path}/{filename}",
|
|
1315
|
+
reporthook=download_hook(t),
|
|
1316
|
+
data=None,
|
|
1317
|
+
)
|
|
1318
|
+
break
|
|
1319
|
+
except:
|
|
1320
|
+
filename = filename.split("/")
|
|
1321
|
+
name += "/" + "/".join(
|
|
1322
|
+
[filename[i] for i in range(len(filename) - 1)]
|
|
1323
|
+
)
|
|
1324
|
+
filename = filename[-1]
|
|
1325
|
+
if not os.path.isdir(f"{name}/{filename}"):
|
|
1326
|
+
os.makedirs(f"{name}/")
|
|
1327
|
+
print(f"File {filename} downloaded successfully to {download_path}.")
|
|
1328
|
+
return
|
|
1329
|
+
|
|
1330
|
+
def get_search_result_file_url(self, analysis_id: str, filename: str):
|
|
1270
1331
|
"""
|
|
1271
1332
|
Given an analysis id and a analysis result filename, this function returns the signed URL for the file.
|
|
1272
1333
|
|
|
@@ -1280,21 +1341,29 @@ class SeerSDK:
|
|
|
1280
1341
|
|
|
1281
1342
|
Returns
|
|
1282
1343
|
-------
|
|
1283
|
-
file_url: dict
|
|
1284
|
-
|
|
1344
|
+
file_url: dict[str, str]
|
|
1345
|
+
Dictionary containing the 'url' and 'filename' of the file.
|
|
1285
1346
|
"""
|
|
1347
|
+
if "." in filename:
|
|
1348
|
+
filename = ".".join(filename.split(".")[:-1])
|
|
1349
|
+
filename = filename.casefold()
|
|
1286
1350
|
|
|
1287
1351
|
# Allow user to pass in filenames without an extension.
|
|
1288
|
-
analysis_result_files = self.
|
|
1352
|
+
analysis_result_files = self.list_search_result_files(analysis_id)
|
|
1289
1353
|
analysis_result_files_prefix_mapper = {
|
|
1290
|
-
".".join(x.split(".")[:-1]): x
|
|
1354
|
+
(".".join(x.split(".")[:-1])).casefold(): x
|
|
1355
|
+
for x in analysis_result_files
|
|
1291
1356
|
}
|
|
1292
1357
|
if filename in analysis_result_files_prefix_mapper:
|
|
1293
1358
|
filename = analysis_result_files_prefix_mapper[filename]
|
|
1359
|
+
else:
|
|
1360
|
+
raise ValueError(
|
|
1361
|
+
f"Filename {filename} not among the available analysis result files. Please use SeerSDK.list_search_result_files('{analysis_id}') to see available files for this analysis."
|
|
1362
|
+
)
|
|
1294
1363
|
|
|
1295
|
-
analysis_metadata = self.
|
|
1364
|
+
analysis_metadata = self.get_analyses(analysis_id)[0]
|
|
1296
1365
|
if analysis_metadata.get("status") in ["Failed", None]:
|
|
1297
|
-
raise ValueError("Cannot generate links for failed
|
|
1366
|
+
raise ValueError("Cannot generate links for failed searches.")
|
|
1298
1367
|
with self._get_auth_session() as s:
|
|
1299
1368
|
file_url = s.post(
|
|
1300
1369
|
f"{self._auth.url}api/v1/analysisResultFiles/getUrl",
|
|
@@ -1307,8 +1376,11 @@ class SeerSDK:
|
|
|
1307
1376
|
response = file_url.json()
|
|
1308
1377
|
if not response.get("url"):
|
|
1309
1378
|
raise ValueError(f"File {filename} not found.")
|
|
1379
|
+
|
|
1380
|
+
response["filename"] = filename
|
|
1310
1381
|
return response
|
|
1311
1382
|
|
|
1383
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="1.0.0")
|
|
1312
1384
|
def get_analysis_result_files(
|
|
1313
1385
|
self,
|
|
1314
1386
|
analysis_id: str,
|
|
@@ -1339,7 +1411,7 @@ class SeerSDK:
|
|
|
1339
1411
|
|
|
1340
1412
|
Returns
|
|
1341
1413
|
-------
|
|
1342
|
-
links: dict
|
|
1414
|
+
links: dict[str, pd.DataFrame]
|
|
1343
1415
|
Contains dataframe objects for the requested files. If a filename is not found, it is skipped.
|
|
1344
1416
|
|
|
1345
1417
|
|
|
@@ -1389,7 +1461,7 @@ class SeerSDK:
|
|
|
1389
1461
|
|
|
1390
1462
|
filenames = set(filenames)
|
|
1391
1463
|
# Allow user to pass in filenames without an extension.
|
|
1392
|
-
analysis_result_files = self.
|
|
1464
|
+
analysis_result_files = self.list_search_result_files(analysis_id)
|
|
1393
1465
|
analysis_result_files_prefix_mapper = {
|
|
1394
1466
|
".".join(x.split(".")[:-1]): x for x in analysis_result_files
|
|
1395
1467
|
}
|
|
@@ -1426,7 +1498,7 @@ class SeerSDK:
|
|
|
1426
1498
|
links["peptide_panel.tsv"] = peptide_data["panelLink"]["url"]
|
|
1427
1499
|
else:
|
|
1428
1500
|
try:
|
|
1429
|
-
links[filename] = self.
|
|
1501
|
+
links[filename] = self._get_search_result_file_url(
|
|
1430
1502
|
analysis_id, filename
|
|
1431
1503
|
)["url"]
|
|
1432
1504
|
except Exception as e:
|
|
@@ -1451,6 +1523,7 @@ class SeerSDK:
|
|
|
1451
1523
|
|
|
1452
1524
|
return links
|
|
1453
1525
|
|
|
1526
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="1.0.0")
|
|
1454
1527
|
def get_analysis_result(
|
|
1455
1528
|
self,
|
|
1456
1529
|
analysis_id: str,
|
|
@@ -1522,7 +1595,7 @@ class SeerSDK:
|
|
|
1522
1595
|
}
|
|
1523
1596
|
|
|
1524
1597
|
if diann_report:
|
|
1525
|
-
diann_report_url = self.
|
|
1598
|
+
diann_report_url = self._get_search_result_file_url(
|
|
1526
1599
|
analysis_id, "report.tsv"
|
|
1527
1600
|
)
|
|
1528
1601
|
links["diann_report"] = url_to_df(diann_report_url["url"])
|
|
@@ -1578,7 +1651,7 @@ class SeerSDK:
|
|
|
1578
1651
|
raise ValueError("Analysis id cannot be empty.")
|
|
1579
1652
|
|
|
1580
1653
|
try:
|
|
1581
|
-
res = self.
|
|
1654
|
+
res = self.get_analyses(analysis_id)
|
|
1582
1655
|
except ValueError:
|
|
1583
1656
|
return ValueError("Analysis not found. Your ID could be incorrect")
|
|
1584
1657
|
|
|
@@ -1597,7 +1670,7 @@ class SeerSDK:
|
|
|
1597
1670
|
|
|
1598
1671
|
Returns
|
|
1599
1672
|
-------
|
|
1600
|
-
list
|
|
1673
|
+
list[str]
|
|
1601
1674
|
Contains the list of files in the folder.
|
|
1602
1675
|
|
|
1603
1676
|
Examples
|
|
@@ -1652,8 +1725,8 @@ class SeerSDK:
|
|
|
1652
1725
|
|
|
1653
1726
|
Returns
|
|
1654
1727
|
-------
|
|
1655
|
-
message: dict
|
|
1656
|
-
Contains the message whether the files were downloaded or not.
|
|
1728
|
+
message: dict[str, str]
|
|
1729
|
+
Contains the 'message' whether the files were downloaded or not.
|
|
1657
1730
|
"""
|
|
1658
1731
|
|
|
1659
1732
|
urls = []
|
|
@@ -1756,6 +1829,11 @@ class SeerSDK:
|
|
|
1756
1829
|
**kwargs : dict, optional
|
|
1757
1830
|
Search keyword parameters to be passed in. Acceptable values are 'name' or 'description'.
|
|
1758
1831
|
|
|
1832
|
+
Returns
|
|
1833
|
+
-------
|
|
1834
|
+
res : list[dict]
|
|
1835
|
+
A list of dictionaries containing the group analysis objects.
|
|
1836
|
+
|
|
1759
1837
|
"""
|
|
1760
1838
|
params = {"analysisid": analysis_id}
|
|
1761
1839
|
if kwargs and not group_analysis_id:
|
|
@@ -1807,7 +1885,7 @@ class SeerSDK:
|
|
|
1807
1885
|
Returns
|
|
1808
1886
|
-------
|
|
1809
1887
|
res : dict
|
|
1810
|
-
A dictionary containing the group analysis
|
|
1888
|
+
A dictionary containing the group analysis object.
|
|
1811
1889
|
|
|
1812
1890
|
Examples
|
|
1813
1891
|
-------
|
|
@@ -1961,7 +2039,7 @@ class SeerSDK:
|
|
|
1961
2039
|
analysis_id (str): ID of the analysis.
|
|
1962
2040
|
feature_ids (list[str], optional): Filter result object to a set of ids. Defaults to [].
|
|
1963
2041
|
show_significant_only (bool, optional): Mark true if only significant results are to be returned. Defaults to False.
|
|
1964
|
-
as_df (bool, optional):
|
|
2042
|
+
as_df (bool, optional): whether the result should be converted to a DataFrame. Defaults to False.
|
|
1965
2043
|
volcano_plot (bool, optional): Mark true to include the volcano plot data in the return object. Defaults to False.
|
|
1966
2044
|
cached (bool, optional): Mark true to return volcano plot data as a VolcanoPlotBuilder object. No effect if volcano_plot flag is marked false. Defaults to False.
|
|
1967
2045
|
|
|
@@ -1983,8 +2061,10 @@ class SeerSDK:
|
|
|
1983
2061
|
|
|
1984
2062
|
protein_peptide_gene_map = builder.protein_gene_map
|
|
1985
2063
|
|
|
1986
|
-
# API call 2 - get analysis samples
|
|
1987
|
-
samples_metadata = self.
|
|
2064
|
+
# API call 2 - get analysis samples to get condition
|
|
2065
|
+
samples_metadata = self._get_analysis_samples(
|
|
2066
|
+
analysis_id=analysis_id
|
|
2067
|
+
)
|
|
1988
2068
|
|
|
1989
2069
|
json = {"analysisId": analysis_id}
|
|
1990
2070
|
if feature_ids:
|
|
@@ -2021,7 +2101,7 @@ class SeerSDK:
|
|
|
2021
2101
|
if x[feature_type_index] in protein_peptide_gene_map
|
|
2022
2102
|
]
|
|
2023
2103
|
sample_id_condition = {
|
|
2024
|
-
x["id"]: x["condition"] for x in samples_metadata
|
|
2104
|
+
x["id"]: x["condition"] for x in samples_metadata
|
|
2025
2105
|
}
|
|
2026
2106
|
|
|
2027
2107
|
if show_significant_only:
|
|
@@ -2062,7 +2142,7 @@ class SeerSDK:
|
|
|
2062
2142
|
box_plot (bool, optional): Mark true to include box plot data in the return object. Defaults to False.
|
|
2063
2143
|
|
|
2064
2144
|
Returns:
|
|
2065
|
-
dict: A dictionary containing the volcano plot and optionally box plot data for each group analysis.
|
|
2145
|
+
dict[str, pd.DataFrame]: A dictionary containing the volcano plot and optionally box plot data for each group analysis.
|
|
2066
2146
|
"""
|
|
2067
2147
|
group_analysis_ids = [
|
|
2068
2148
|
x["id"]
|
|
@@ -2118,8 +2198,8 @@ class SeerSDK:
|
|
|
2118
2198
|
ValueError: Invalid type provided.
|
|
2119
2199
|
ServerError: Could not fetch PCA data.
|
|
2120
2200
|
Returns:
|
|
2121
|
-
dict
|
|
2122
|
-
|
|
2201
|
+
dict[str, list|float]
|
|
2202
|
+
Returns response object containing 'xContributionRatio' (float), 'yContributionRatio' (float), 'samples' (list[dict]), and 'points' (list[float]).
|
|
2123
2203
|
"""
|
|
2124
2204
|
if not analysis_ids:
|
|
2125
2205
|
raise ValueError("Analysis IDs cannot be empty.")
|
|
@@ -2164,7 +2244,7 @@ class SeerSDK:
|
|
|
2164
2244
|
type (str): Type of data to be fetched. Must be either 'protein' or 'peptide'.
|
|
2165
2245
|
sample_ids (list[str], optional): IDs of the samples of interest.
|
|
2166
2246
|
hide_control (bool, optional): Mark true if controls are to be excluded. Defaults to False.
|
|
2167
|
-
as_df (bool, optional):
|
|
2247
|
+
as_df (bool, optional): whether the result should be converted to a DataFrame. Defaults to False.
|
|
2168
2248
|
Raises:
|
|
2169
2249
|
ValueError: No analysis IDs provided.
|
|
2170
2250
|
ValueError: No sample IDs provided.
|
|
@@ -2491,7 +2571,7 @@ class SeerSDK:
|
|
|
2491
2571
|
fold_change_threshold (float, optional): Cutoff value for the fold change to determine significance. Defaults to 1.
|
|
2492
2572
|
label_by (str, optional): Metric to sort result data. Defaults to "fold_change".
|
|
2493
2573
|
cached (bool, optional): Return a VolcanoPlotBuilder object for calculation reuse. Defaults to False.
|
|
2494
|
-
as_df (bool, optional):
|
|
2574
|
+
as_df (bool, optional): whether the result should be converted to a DataFrame. Defaults to False.
|
|
2495
2575
|
|
|
2496
2576
|
Raises:
|
|
2497
2577
|
ServerError - could not fetch group analysis results.
|
|
@@ -2521,29 +2601,51 @@ class SeerSDK:
|
|
|
2521
2601
|
else:
|
|
2522
2602
|
return obj.volcano_plot
|
|
2523
2603
|
|
|
2524
|
-
def
|
|
2604
|
+
def _get_analysis_samples(
|
|
2605
|
+
self, analysis_id: str = None, analysis_name: str = None, as_df=False
|
|
2606
|
+
):
|
|
2525
2607
|
"""
|
|
2526
|
-
Get the samples associated with a given analysis
|
|
2608
|
+
Get the samples associated with a given analysis.
|
|
2527
2609
|
|
|
2528
2610
|
Args:
|
|
2529
|
-
analysis_id (str):
|
|
2611
|
+
analysis_id (str): UUID identifier of the analysis. Defaults to None.
|
|
2612
|
+
analysis_name (str): Name of the analysis. Defaults to None.
|
|
2613
|
+
as_df (bool) : whether the result should be converted to a DataFrame. Defaults to False.
|
|
2530
2614
|
|
|
2531
2615
|
Raises:
|
|
2532
2616
|
ServerError - could not retrieve samples for analysis.
|
|
2533
2617
|
Returns:
|
|
2534
|
-
dict:
|
|
2618
|
+
list[dict] : a list of samples associated with the analysis.
|
|
2535
2619
|
"""
|
|
2536
|
-
if not analysis_id:
|
|
2537
|
-
raise ValueError("Analysis ID cannot be empty.")
|
|
2538
2620
|
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
samples = s.get(URL)
|
|
2621
|
+
if not analysis_id and not analysis_name:
|
|
2622
|
+
raise ValueError("Analysis cannot be empty.")
|
|
2542
2623
|
|
|
2543
|
-
|
|
2544
|
-
|
|
2624
|
+
if analysis_id:
|
|
2625
|
+
rows = [{"id": analysis_id}]
|
|
2626
|
+
else:
|
|
2627
|
+
rows = self.get_analyses(analysis_name=analysis_name)
|
|
2628
|
+
|
|
2629
|
+
resp = []
|
|
2630
|
+
for row in rows:
|
|
2631
|
+
URL = f"{self._auth.url}api/v1/analyses/samples/{row['id']}"
|
|
2632
|
+
with self._get_auth_session() as s:
|
|
2633
|
+
samples = s.get(URL)
|
|
2634
|
+
try:
|
|
2635
|
+
samples.raise_for_status()
|
|
2636
|
+
obj = samples.json()[0]
|
|
2637
|
+
resp += obj["samples"]
|
|
2638
|
+
except:
|
|
2639
|
+
continue
|
|
2640
|
+
|
|
2641
|
+
if not resp:
|
|
2642
|
+
raise ServerError(
|
|
2643
|
+
f"Could not retrieve samples for analysis {analysis_id or analysis_name}."
|
|
2644
|
+
)
|
|
2545
2645
|
|
|
2546
|
-
|
|
2646
|
+
resp = pd.DataFrame(resp)
|
|
2647
|
+
resp.drop_duplicates(subset=["id"], inplace=True)
|
|
2648
|
+
return resp if as_df else resp.to_dict(orient="records")
|
|
2547
2649
|
|
|
2548
2650
|
def get_analysis_protocol_fasta(self, analysis_id, download_path=None):
|
|
2549
2651
|
if not analysis_id:
|
|
@@ -2553,7 +2655,7 @@ class SeerSDK:
|
|
|
2553
2655
|
download_path = os.getcwd()
|
|
2554
2656
|
|
|
2555
2657
|
try:
|
|
2556
|
-
analysis_protocol_id = self.
|
|
2658
|
+
analysis_protocol_id = self.get_analyses(analysis_id)[0][
|
|
2557
2659
|
"analysis_protocol_id"
|
|
2558
2660
|
]
|
|
2559
2661
|
except (IndexError, KeyError):
|
seer_pas_sdk/core/unsupported.py
CHANGED
|
@@ -277,7 +277,7 @@ class _UnsupportedSDK(_SeerSDK):
|
|
|
277
277
|
samples = (
|
|
278
278
|
x["id"]
|
|
279
279
|
for plate_id in plates
|
|
280
|
-
for x in self.
|
|
280
|
+
for x in self.get_samples(plate_id=plate_id)
|
|
281
281
|
)
|
|
282
282
|
|
|
283
283
|
return self.add_samples_to_project(
|
|
@@ -743,7 +743,7 @@ class _UnsupportedSDK(_SeerSDK):
|
|
|
743
743
|
if sample_ids:
|
|
744
744
|
valid_ids = [
|
|
745
745
|
entry["id"]
|
|
746
|
-
for entry in self.
|
|
746
|
+
for entry in self.get_samples(project_id=project_id)
|
|
747
747
|
]
|
|
748
748
|
|
|
749
749
|
for sample_id in sample_ids:
|
|
@@ -1166,7 +1166,7 @@ class _UnsupportedSDK(_SeerSDK):
|
|
|
1166
1166
|
raise ValueError("Analysis ID cannot be empty.")
|
|
1167
1167
|
|
|
1168
1168
|
try:
|
|
1169
|
-
valid_analysis = self.
|
|
1169
|
+
valid_analysis = self.get_analyses(analysis_id)[0]
|
|
1170
1170
|
except:
|
|
1171
1171
|
raise ValueError(
|
|
1172
1172
|
"Invalid analysis ID. Please check if the analysis ID is valid or the backend is running."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seer-pas-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: SDK for Seer Proteograph Analysis Suite (PAS)
|
|
5
5
|
Author-email: Ryan Sun <rsun@seer.bio>
|
|
6
6
|
License:
|
|
@@ -199,6 +199,7 @@ Requires-Dist: PyJWT>=2.8.0
|
|
|
199
199
|
Requires-Dist: python-dotenv>=1.0.0
|
|
200
200
|
Requires-Dist: Requests>=2.31.0
|
|
201
201
|
Requires-Dist: tqdm>=4.65.0
|
|
202
|
+
Requires-Dist: deprecation
|
|
202
203
|
Dynamic: license-file
|
|
203
204
|
|
|
204
205
|
# Seer PAS Python SDK
|
|
@@ -5,14 +5,14 @@ seer_pas_sdk/common/__init__.py,sha256=jA5qm-t9x3qXMp5Q6v7vV2kZdLw32-lnbtf0fPY4l
|
|
|
5
5
|
seer_pas_sdk/common/errors.py,sha256=4HFORWnaQQCMXRE8kwdsJWvQRB_3KFEZ7yMb391e4gA,142
|
|
6
6
|
seer_pas_sdk/common/groupanalysis.py,sha256=DxB-gbQfYzl7p9MTYWDIqghcH-IeakzdYdrRZrlIHek,1730
|
|
7
7
|
seer_pas_sdk/core/__init__.py,sha256=rxbKgg-Qe24OaxX2zyHHYPYgDCTEKE_-41bB2wvpvL4,25
|
|
8
|
-
seer_pas_sdk/core/sdk.py,sha256=
|
|
9
|
-
seer_pas_sdk/core/unsupported.py,sha256=
|
|
8
|
+
seer_pas_sdk/core/sdk.py,sha256=Wt-cB1hB0dMZLDg0oZvO-NPGzKR70dCAdrZ4xKnC2zo,101286
|
|
9
|
+
seer_pas_sdk/core/unsupported.py,sha256=XAPZ3tidqjnsgftf3NUdTGIzvnsjHy0e_eGRCAo6GPo,59890
|
|
10
10
|
seer_pas_sdk/objects/__init__.py,sha256=HJLS6sOr7DfzdI14fv5dWcITEj5QQsKcdfED3YNvUrY,107
|
|
11
11
|
seer_pas_sdk/objects/groupanalysis.py,sha256=x3D_5NmYBoPDilNCQqUoCFARIfIeUq4FBY3_N6u8tfM,994
|
|
12
12
|
seer_pas_sdk/objects/platemap.py,sha256=8IvJPAecs_e_FyqibzhCw-O4zjCFnf-zMUp_5krTEsg,5864
|
|
13
13
|
seer_pas_sdk/objects/volcanoplot.py,sha256=tKuCWDIdoO8FLJlhpXhuwHn0aMYnvudTugxAslDXyGs,9357
|
|
14
|
-
seer_pas_sdk-0.
|
|
15
|
-
seer_pas_sdk-0.
|
|
16
|
-
seer_pas_sdk-0.
|
|
17
|
-
seer_pas_sdk-0.
|
|
18
|
-
seer_pas_sdk-0.
|
|
14
|
+
seer_pas_sdk-0.3.0.dist-info/licenses/LICENSE.txt,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
|
|
15
|
+
seer_pas_sdk-0.3.0.dist-info/METADATA,sha256=dpaBy3hI3vwikAThyVZeWjF5qNyiHrJhwEVSbmJA9rU,13448
|
|
16
|
+
seer_pas_sdk-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
seer_pas_sdk-0.3.0.dist-info/top_level.txt,sha256=-2kZ-KFMGtXwr8H1O5llMKlcJ8gRKohEmrIvazXB61s,13
|
|
18
|
+
seer_pas_sdk-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|