seer-pas-sdk 0.3.4__py3-none-any.whl → 1.1.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/auth/auth.py +174 -15
- seer_pas_sdk/common/__init__.py +46 -5
- seer_pas_sdk/core/sdk.py +1474 -183
- seer_pas_sdk/core/unsupported.py +415 -223
- seer_pas_sdk/objects/__init__.py +1 -0
- seer_pas_sdk/objects/headers.py +144 -0
- seer_pas_sdk/objects/volcanoplot.py +3 -2
- {seer_pas_sdk-0.3.4.dist-info → seer_pas_sdk-1.1.0.dist-info}/METADATA +1 -2
- seer_pas_sdk-1.1.0.dist-info/RECORD +19 -0
- seer_pas_sdk-0.3.4.dist-info/RECORD +0 -18
- {seer_pas_sdk-0.3.4.dist-info → seer_pas_sdk-1.1.0.dist-info}/WHEEL +0 -0
- {seer_pas_sdk-0.3.4.dist-info → seer_pas_sdk-1.1.0.dist-info}/licenses/LICENSE.txt +0 -0
- {seer_pas_sdk-0.3.4.dist-info → seer_pas_sdk-1.1.0.dist-info}/top_level.txt +0 -0
seer_pas_sdk/core/sdk.py
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
from tqdm import tqdm
|
|
2
2
|
|
|
3
3
|
import deprecation
|
|
4
|
+
import numpy as np
|
|
4
5
|
import os
|
|
5
|
-
import jwt
|
|
6
6
|
import requests
|
|
7
7
|
import urllib.request
|
|
8
8
|
import ssl
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
from typing import List as _List, Tuple as _Tuple
|
|
11
12
|
|
|
12
13
|
from ..common import *
|
|
13
14
|
from ..auth import Auth
|
|
14
15
|
from ..objects.volcanoplot import VolcanoPlotBuilder
|
|
16
|
+
from ..objects.headers import *
|
|
17
|
+
|
|
18
|
+
import warnings
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
class SeerSDK:
|
|
@@ -31,7 +35,7 @@ class SeerSDK:
|
|
|
31
35
|
try:
|
|
32
36
|
self._auth = Auth(username, password, instance)
|
|
33
37
|
|
|
34
|
-
self._auth.
|
|
38
|
+
self._auth._login()
|
|
35
39
|
print(f"User '{username}' logged in.\n")
|
|
36
40
|
|
|
37
41
|
if not tenant:
|
|
@@ -45,16 +49,43 @@ class SeerSDK:
|
|
|
45
49
|
print("Logging into home tenant...")
|
|
46
50
|
# If an error occurs while directing the user to a tenant, default to home tenant.
|
|
47
51
|
print(f"You are now active in {self.get_active_tenant_name()}")
|
|
52
|
+
except ServerError as e:
|
|
53
|
+
raise e
|
|
48
54
|
except Exception as e:
|
|
49
55
|
raise ValueError(
|
|
50
56
|
f"Could not log in.\nPlease check your credentials and/or instance: {e}."
|
|
51
57
|
)
|
|
52
58
|
|
|
53
|
-
def
|
|
59
|
+
def logout(self):
|
|
60
|
+
"""
|
|
61
|
+
Perform a logout operation for the current user of the SDK instance.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
success : bool
|
|
66
|
+
Boolean denoting whether the logout operation was successful.
|
|
67
|
+
"""
|
|
68
|
+
if self._auth.has_valid_token():
|
|
69
|
+
self._auth._logout()
|
|
70
|
+
return True
|
|
71
|
+
else:
|
|
72
|
+
print("The user's session is expired. No action taken.")
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
def __del__(self):
|
|
76
|
+
"""
|
|
77
|
+
Destructor for the SeerSDK class. Logs out the user when the object is deleted.
|
|
78
|
+
"""
|
|
79
|
+
if self._auth.has_valid_token():
|
|
80
|
+
self._auth._logout()
|
|
81
|
+
|
|
82
|
+
def _get_auth_headers(self, caller, use_multi_tenant=True):
|
|
54
83
|
id_token, access_token = self._auth.get_token()
|
|
55
84
|
header = {
|
|
56
85
|
"Authorization": id_token,
|
|
57
86
|
"Access-Token": access_token,
|
|
87
|
+
"x-seer-source": "sdk",
|
|
88
|
+
"x-seer-id": f"{self._auth.version}/{caller}",
|
|
58
89
|
}
|
|
59
90
|
if use_multi_tenant:
|
|
60
91
|
multi_tenant = {
|
|
@@ -64,10 +95,10 @@ class SeerSDK:
|
|
|
64
95
|
header.update(multi_tenant)
|
|
65
96
|
return header
|
|
66
97
|
|
|
67
|
-
def _get_auth_session(self, use_multi_tenant=True):
|
|
98
|
+
def _get_auth_session(self, caller, use_multi_tenant=True):
|
|
68
99
|
sess = requests.Session()
|
|
69
100
|
|
|
70
|
-
sess.headers.update(self._get_auth_headers(use_multi_tenant))
|
|
101
|
+
sess.headers.update(self._get_auth_headers(caller, use_multi_tenant))
|
|
71
102
|
|
|
72
103
|
return sess
|
|
73
104
|
|
|
@@ -80,7 +111,7 @@ class SeerSDK:
|
|
|
80
111
|
response : list[dict]
|
|
81
112
|
A list of tenant objects pertaining to the user.
|
|
82
113
|
"""
|
|
83
|
-
with self._get_auth_session() as s:
|
|
114
|
+
with self._get_auth_session("getusertenants") as s:
|
|
84
115
|
response = s.get(f"{self._auth.url}api/v1/usertenants")
|
|
85
116
|
|
|
86
117
|
if response.status_code != 200:
|
|
@@ -162,7 +193,7 @@ class SeerSDK:
|
|
|
162
193
|
"Invalid tenant identifier. Tenant was not switched."
|
|
163
194
|
)
|
|
164
195
|
|
|
165
|
-
with self._get_auth_session() as s:
|
|
196
|
+
with self._get_auth_session("switchtenant") as s:
|
|
166
197
|
response = s.put(
|
|
167
198
|
self._auth.url + "api/v1/users/tenant",
|
|
168
199
|
json={
|
|
@@ -242,7 +273,7 @@ class SeerSDK:
|
|
|
242
273
|
|
|
243
274
|
URL = f"{self._auth.url}api/v1/usergroups"
|
|
244
275
|
|
|
245
|
-
with self._get_auth_session() as s:
|
|
276
|
+
with self._get_auth_session("getspaces") as s:
|
|
246
277
|
spaces = s.get(URL)
|
|
247
278
|
|
|
248
279
|
if spaces.status_code != 200:
|
|
@@ -251,6 +282,11 @@ class SeerSDK:
|
|
|
251
282
|
)
|
|
252
283
|
return spaces.json()
|
|
253
284
|
|
|
285
|
+
@deprecation.deprecated(
|
|
286
|
+
deprecated_in="1.1.0",
|
|
287
|
+
removed_in="2.0.0",
|
|
288
|
+
details="This method is deprecated and will be removed in a future release. Use `find_plates` instead.",
|
|
289
|
+
)
|
|
254
290
|
def get_plates(
|
|
255
291
|
self, plate_id: str = None, plate_name: str = None, as_df: bool = False
|
|
256
292
|
):
|
|
@@ -293,7 +329,7 @@ class SeerSDK:
|
|
|
293
329
|
938 5b05d440-6610-11ea-96e3-d5a4dab4ebf6 ... None
|
|
294
330
|
939 9872e3f0-544e-11ea-ad9e-1991e0725494 ... None
|
|
295
331
|
|
|
296
|
-
>>> seer_sdk.
|
|
332
|
+
>>> seer_sdk.get_plates(id="YOUR_PLATE_ID_HERE")
|
|
297
333
|
>>> [{ "id": ... }]
|
|
298
334
|
"""
|
|
299
335
|
|
|
@@ -307,7 +343,7 @@ class SeerSDK:
|
|
|
307
343
|
else:
|
|
308
344
|
params = dict()
|
|
309
345
|
|
|
310
|
-
with self._get_auth_session() as s:
|
|
346
|
+
with self._get_auth_session("getplates") as s:
|
|
311
347
|
|
|
312
348
|
plates = s.get(
|
|
313
349
|
f"{URL}/{plate_id}" if plate_id else URL,
|
|
@@ -327,6 +363,172 @@ class SeerSDK:
|
|
|
327
363
|
|
|
328
364
|
return res if not as_df else dict_to_df(res)
|
|
329
365
|
|
|
366
|
+
def get_plate(self, plate_id: str = None, plate_name: str = None):
|
|
367
|
+
"""
|
|
368
|
+
Fetches a plate.
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
plate_id : str, optional
|
|
373
|
+
ID of the plate to be fetched, defaulted to None.
|
|
374
|
+
|
|
375
|
+
plate_name : str, optional
|
|
376
|
+
Name of the plate to be fetched, defaulted to None.
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
plate: dict
|
|
381
|
+
A plate object.
|
|
382
|
+
"""
|
|
383
|
+
if not (bool(plate_id) ^ bool(plate_name)):
|
|
384
|
+
raise ValueError(
|
|
385
|
+
"You must provide either plate_id or plate_name, but not both."
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if plate_id:
|
|
389
|
+
URL = f"{self._auth.url}api/v1/plates/{plate_id}"
|
|
390
|
+
with self._get_auth_session("getplate") as s:
|
|
391
|
+
plates = s.get(URL)
|
|
392
|
+
if plates.status_code != 200:
|
|
393
|
+
raise ValueError(
|
|
394
|
+
"Invalid request. Please check your parameters."
|
|
395
|
+
)
|
|
396
|
+
res = plates.json()
|
|
397
|
+
spaces = {
|
|
398
|
+
x["id"]: x["usergroup_name"] for x in self.get_spaces()
|
|
399
|
+
}
|
|
400
|
+
if "tenant_id" in res:
|
|
401
|
+
del res["tenant_id"]
|
|
402
|
+
if "user_group" in res:
|
|
403
|
+
res["space"] = spaces.get(res["user_group"], "General")
|
|
404
|
+
del res["user_group"]
|
|
405
|
+
if "id" in res:
|
|
406
|
+
res["plate_uuid"] = res["id"]
|
|
407
|
+
return res
|
|
408
|
+
else:
|
|
409
|
+
res = self.find_plates(plate_name=plate_name)
|
|
410
|
+
if not res:
|
|
411
|
+
raise ValueError(f"No plate found with name '{plate_name}'.")
|
|
412
|
+
elif len(res) > 1:
|
|
413
|
+
raise ValueError(
|
|
414
|
+
f"Multiple plates found with name '{plate_name}'. Please specify a plate_id."
|
|
415
|
+
)
|
|
416
|
+
else:
|
|
417
|
+
return res[0]
|
|
418
|
+
|
|
419
|
+
def find_plates(
|
|
420
|
+
self,
|
|
421
|
+
plate_id: str = None,
|
|
422
|
+
plate_name: str = None,
|
|
423
|
+
project_id: str = None,
|
|
424
|
+
project_name: str = None,
|
|
425
|
+
as_df: bool = False,
|
|
426
|
+
):
|
|
427
|
+
"""
|
|
428
|
+
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.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
plate_id : str, optional
|
|
433
|
+
ID of the plate to be fetched, defaulted to None.
|
|
434
|
+
plate_name : str, optional
|
|
435
|
+
Name of the plate to be fetched, defaulted to None.
|
|
436
|
+
project_id: str, optional
|
|
437
|
+
ID of the project to filter plates by, defaulted to None.
|
|
438
|
+
project_name : str, optional
|
|
439
|
+
Name of the project to filter plates by, defaulted to None.
|
|
440
|
+
as_df: bool
|
|
441
|
+
whether the result should be converted to a DataFrame, defaulted to None.
|
|
442
|
+
|
|
443
|
+
Returns
|
|
444
|
+
-------
|
|
445
|
+
plates: list[dict] or DataFrame
|
|
446
|
+
List/DataFrame of plate objects for the authenticated user.
|
|
447
|
+
|
|
448
|
+
Examples
|
|
449
|
+
-------
|
|
450
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
451
|
+
>>> seer_sdk = SeerSDK()
|
|
452
|
+
>>> seer_sdk.find_plates()
|
|
453
|
+
>>> [
|
|
454
|
+
{ "id": ... },
|
|
455
|
+
{ "id": ... },
|
|
456
|
+
...
|
|
457
|
+
]
|
|
458
|
+
>>> seer_sdk.find_plates(as_df=True)
|
|
459
|
+
>>> id ... user_group
|
|
460
|
+
0 a7c12190-15da-11ee-bdf1-bbaa73585acf ... None
|
|
461
|
+
1 8c3b1480-15da-11ee-bdf1-bbaa73585acf ... None
|
|
462
|
+
2 6f158840-15da-11ee-bdf1-bbaa73585acf ... None
|
|
463
|
+
3 1a8a2920-15da-11ee-bdf1-bbaa73585acf ... None
|
|
464
|
+
4 7ab47f40-15d9-11ee-bdf1-bbaa73585acf ... None
|
|
465
|
+
.. ... ... ...
|
|
466
|
+
935 8fa91c00-6621-11ea-96e3-d5a4dab4ebf6 ... None
|
|
467
|
+
936 53180b20-6621-11ea-96e3-d5a4dab4ebf6 ... None
|
|
468
|
+
937 5c31fe90-6618-11ea-96e3-d5a4dab4ebf6 ... None
|
|
469
|
+
938 5b05d440-6610-11ea-96e3-d5a4dab4ebf6 ... None
|
|
470
|
+
939 9872e3f0-544e-11ea-ad9e-1991e0725494 ... None
|
|
471
|
+
|
|
472
|
+
>>> seer_sdk.find_plates(id="YOUR_PLATE_ID_HERE")
|
|
473
|
+
>>> [{ "id": ... }]
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
URL = f"{self._auth.url}api/v1/plates"
|
|
477
|
+
res = []
|
|
478
|
+
|
|
479
|
+
if project_id or project_name:
|
|
480
|
+
if project_name and not project_id:
|
|
481
|
+
samples = self.find_samples(
|
|
482
|
+
project_name=project_name, as_df=False
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
samples = self.find_samples(project_id=project_id, as_df=False)
|
|
486
|
+
plate_ids = {
|
|
487
|
+
x.get("plate_uuid") for x in samples if "plate_uuid" in x
|
|
488
|
+
}
|
|
489
|
+
res = [self.get_plate(plate_id=x) for x in plate_ids]
|
|
490
|
+
return res if not as_df else dict_to_df(res)
|
|
491
|
+
|
|
492
|
+
if not plate_id and not plate_name:
|
|
493
|
+
params = {"all": "true"}
|
|
494
|
+
elif plate_id:
|
|
495
|
+
params = {"searchFields": "id", "searchItem": plate_id}
|
|
496
|
+
elif plate_name:
|
|
497
|
+
params = {"searchFields": "plate_name", "searchItem": plate_name}
|
|
498
|
+
else:
|
|
499
|
+
params = dict()
|
|
500
|
+
|
|
501
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
502
|
+
with self._get_auth_session("findplates") as s:
|
|
503
|
+
|
|
504
|
+
plates = s.get(
|
|
505
|
+
URL,
|
|
506
|
+
params=params,
|
|
507
|
+
)
|
|
508
|
+
if plates.status_code != 200:
|
|
509
|
+
raise ValueError(
|
|
510
|
+
"Invalid request. Please check your parameters."
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
res = plates.json()["data"]
|
|
514
|
+
|
|
515
|
+
for entry in res:
|
|
516
|
+
if "tenant_id" in entry:
|
|
517
|
+
del entry["tenant_id"]
|
|
518
|
+
if "user_group" in entry:
|
|
519
|
+
entry["space"] = spaces.get(entry["user_group"], "General")
|
|
520
|
+
del entry["user_group"]
|
|
521
|
+
if "id" in entry:
|
|
522
|
+
entry["plate_uuid"] = entry["id"]
|
|
523
|
+
if (not res) and as_df:
|
|
524
|
+
return pd.DataFrame(columns=PLATE_COLUMNS)
|
|
525
|
+
return res if not as_df else dict_to_df(res)
|
|
526
|
+
|
|
527
|
+
@deprecation.deprecated(
|
|
528
|
+
deprecated_in="1.1.0",
|
|
529
|
+
removed_in="2.0.0",
|
|
530
|
+
details="This method is deprecated and will be removed in a future release. Use `find_projects` instead.",
|
|
531
|
+
)
|
|
330
532
|
def get_projects(
|
|
331
533
|
self,
|
|
332
534
|
project_id: str = None,
|
|
@@ -393,7 +595,7 @@ class SeerSDK:
|
|
|
393
595
|
else:
|
|
394
596
|
params = {"searchFields": "id", "searchItem": project_id}
|
|
395
597
|
|
|
396
|
-
with self._get_auth_session() as s:
|
|
598
|
+
with self._get_auth_session("getprojects") as s:
|
|
397
599
|
|
|
398
600
|
projects = s.get(URL, params=params)
|
|
399
601
|
if projects.status_code != 200:
|
|
@@ -416,8 +618,172 @@ class SeerSDK:
|
|
|
416
618
|
entry["raw_file_path"] = entry["raw_file_path"][
|
|
417
619
|
location(entry["raw_file_path"]) :
|
|
418
620
|
]
|
|
621
|
+
|
|
622
|
+
return res if not as_df else dict_to_df(res)
|
|
623
|
+
|
|
624
|
+
def get_project(self, project_id: str = None, project_name: str = None):
|
|
625
|
+
"""
|
|
626
|
+
Fetches a project.
|
|
627
|
+
|
|
628
|
+
Parameters
|
|
629
|
+
----------
|
|
630
|
+
project_id: str, optional
|
|
631
|
+
ID of the project to be fetched, defaulted to None.
|
|
632
|
+
|
|
633
|
+
project_name: str, optional
|
|
634
|
+
Name of the project to be fetched, defaulted to None.
|
|
635
|
+
|
|
636
|
+
Returns
|
|
637
|
+
-------
|
|
638
|
+
projects: dict
|
|
639
|
+
A project object.
|
|
640
|
+
"""
|
|
641
|
+
if not (bool(project_id) ^ bool(project_name)):
|
|
642
|
+
raise ValueError(
|
|
643
|
+
"You must provide either project_id or project_name, but not both."
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
if project_id:
|
|
647
|
+
URL = f"{self._auth.url}api/v1/projects/{project_id}"
|
|
648
|
+
with self._get_auth_session("getproject") as s:
|
|
649
|
+
projects = s.get(URL)
|
|
650
|
+
if projects.status_code != 200:
|
|
651
|
+
raise ValueError(
|
|
652
|
+
"Invalid request. Please check your parameters."
|
|
653
|
+
)
|
|
654
|
+
res = projects.json()
|
|
655
|
+
spaces = {
|
|
656
|
+
x["id"]: x["usergroup_name"] for x in self.get_spaces()
|
|
657
|
+
}
|
|
658
|
+
if "tenant_id" in res:
|
|
659
|
+
del res["tenant_id"]
|
|
660
|
+
if "user_group" in res:
|
|
661
|
+
res["space"] = spaces.get(res["user_group"], "General")
|
|
662
|
+
del res["user_group"]
|
|
663
|
+
plate_ids = {
|
|
664
|
+
x["plate_uuid"]
|
|
665
|
+
for x in self.find_samples(project_id=res["id"])
|
|
666
|
+
}
|
|
667
|
+
res["plate_uuids"] = list(plate_ids)
|
|
668
|
+
return res
|
|
669
|
+
else:
|
|
670
|
+
res = self.find_projects(project_name=project_name)
|
|
671
|
+
if not res:
|
|
672
|
+
raise ValueError(
|
|
673
|
+
f"No project found with name '{project_name}'."
|
|
674
|
+
)
|
|
675
|
+
elif len(res) > 1:
|
|
676
|
+
raise ValueError(
|
|
677
|
+
f"Multiple projects found with name '{project_name}'. Please specify a project_id."
|
|
678
|
+
)
|
|
679
|
+
else:
|
|
680
|
+
return res[0]
|
|
681
|
+
|
|
682
|
+
def find_projects(
|
|
683
|
+
self,
|
|
684
|
+
project_id: str = None,
|
|
685
|
+
project_name: str = None,
|
|
686
|
+
as_df: bool = False,
|
|
687
|
+
):
|
|
688
|
+
"""
|
|
689
|
+
Fetches a list of projects. If no `project_id` is provided, returns all projects. If `project_id` is provided, returns the project with the given `project_id`, provided it exists.
|
|
690
|
+
|
|
691
|
+
Parameters
|
|
692
|
+
----------
|
|
693
|
+
project_id: str, optional
|
|
694
|
+
Project ID of the project to be fetched, defaulted to None.
|
|
695
|
+
as_df: bool
|
|
696
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
697
|
+
|
|
698
|
+
Returns
|
|
699
|
+
-------
|
|
700
|
+
projects: list[dict] or DataFrame
|
|
701
|
+
DataFrame or list of project objects.
|
|
702
|
+
|
|
703
|
+
Examples
|
|
704
|
+
-------
|
|
705
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
706
|
+
>>> seer_sdk = SeerSDK()
|
|
707
|
+
>>> seer_sdk.get_projects()
|
|
708
|
+
>>> [
|
|
709
|
+
{ "project_name": ... },
|
|
710
|
+
{ "project_name": ... },
|
|
711
|
+
...
|
|
712
|
+
]
|
|
713
|
+
|
|
714
|
+
>>> seer_sdk.get_projects(as_df=True)
|
|
715
|
+
>>> id ... user_group
|
|
716
|
+
0 a7c12190-15da-11ee-bdf1-bbaa73585acf ... None
|
|
717
|
+
1 8c3b1480-15da-11ee-bdf1-bbaa73585acf ... None
|
|
718
|
+
2 6f158840-15da-11ee-bdf1-bbaa73585acf ... None
|
|
719
|
+
3 1a8a2920-15da-11ee-bdf1-bbaa73585acf ... None
|
|
720
|
+
4 7ab47f40-15d9-11ee-bdf1-bbaa73585acf ... None
|
|
721
|
+
.. ... ... ...
|
|
722
|
+
935 8fa91c00-6621-11ea-96e3-d5a4dab4ebf6 ... None
|
|
723
|
+
936 53180b20-6621-11ea-96e3-d5a4dab4ebf6 ... None
|
|
724
|
+
937 5c31fe90-6618-11ea-96e3-d5a4dab4ebf6 ... None
|
|
725
|
+
938 5b05d440-6610-11ea-96e3-d5a4dab4ebf6 ... None
|
|
726
|
+
939 9872e3f0-544e-11ea-ad9e-1991e0725494 ... None
|
|
727
|
+
|
|
728
|
+
>>> seer_sdk.find_projects(project_id="YOUR_PROJECT_ID_HERE")
|
|
729
|
+
>>> [{ "project_name": ... }]
|
|
730
|
+
"""
|
|
731
|
+
|
|
732
|
+
URL = f"{self._auth.url}api/v1/projects"
|
|
733
|
+
res = []
|
|
734
|
+
if not project_id and not project_name:
|
|
735
|
+
params = {"all": "true"}
|
|
736
|
+
elif project_name:
|
|
737
|
+
params = {
|
|
738
|
+
"searchFields": "project_name",
|
|
739
|
+
"searchItem": project_name,
|
|
740
|
+
}
|
|
741
|
+
else:
|
|
742
|
+
params = {"searchFields": "id", "searchItem": project_id}
|
|
743
|
+
|
|
744
|
+
with self._get_auth_session("findprojects") as s:
|
|
745
|
+
|
|
746
|
+
projects = s.get(URL, params=params)
|
|
747
|
+
if projects.status_code != 200:
|
|
748
|
+
raise ValueError(
|
|
749
|
+
"Invalid request. Please check your parameters."
|
|
750
|
+
)
|
|
751
|
+
res = projects.json()["data"]
|
|
752
|
+
|
|
753
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
754
|
+
plate_name_to_plate_uuid = {
|
|
755
|
+
x["plate_name"]: x["plate_uuid"]
|
|
756
|
+
for x in self.find_plates(as_df=False)
|
|
757
|
+
}
|
|
758
|
+
for entry in res:
|
|
759
|
+
if "tenant_id" in entry:
|
|
760
|
+
del entry["tenant_id"]
|
|
761
|
+
|
|
762
|
+
if "raw_file_path" in entry:
|
|
763
|
+
# Simple lambda function to find the third occurrence of '/' in the raw file path
|
|
764
|
+
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
765
|
+
# Slicing the string from the location
|
|
766
|
+
entry["raw_file_path"] = entry["raw_file_path"][
|
|
767
|
+
location(entry["raw_file_path"]) :
|
|
768
|
+
]
|
|
769
|
+
if "user_group" in entry:
|
|
770
|
+
entry["space"] = spaces.get(entry["user_group"], "General")
|
|
771
|
+
del entry["user_group"]
|
|
772
|
+
|
|
773
|
+
if "plates" in entry:
|
|
774
|
+
entry["plate_uuids"] = [
|
|
775
|
+
plate_name_to_plate_uuid[x] for x in entry["plates"]
|
|
776
|
+
]
|
|
777
|
+
|
|
778
|
+
if not res and as_df:
|
|
779
|
+
return pd.DataFrame(columns=PROJECT_COLUMNS)
|
|
419
780
|
return res if not as_df else dict_to_df(res)
|
|
420
781
|
|
|
782
|
+
@deprecation.deprecated(
|
|
783
|
+
deprecated_in="1.1.0",
|
|
784
|
+
removed_in="2.0.0",
|
|
785
|
+
details="This method is deprecated and will be removed in a future release. Use `find_samples` instead.",
|
|
786
|
+
)
|
|
421
787
|
def get_samples(
|
|
422
788
|
self,
|
|
423
789
|
plate_id: str = None,
|
|
@@ -493,10 +859,10 @@ class SeerSDK:
|
|
|
493
859
|
sample_params = {"all": "true"}
|
|
494
860
|
|
|
495
861
|
if project_id or plate_id:
|
|
496
|
-
with self._get_auth_session() as s:
|
|
862
|
+
with self._get_auth_session("getsamples") as s:
|
|
497
863
|
if plate_id:
|
|
498
864
|
try:
|
|
499
|
-
self.
|
|
865
|
+
self.find_plates(plate_id=plate_id)
|
|
500
866
|
except:
|
|
501
867
|
raise ValueError("Plate ID is invalid.")
|
|
502
868
|
sample_params["plateId"] = plate_id
|
|
@@ -530,7 +896,7 @@ class SeerSDK:
|
|
|
530
896
|
)
|
|
531
897
|
else:
|
|
532
898
|
res_df = self._get_analysis_samples(
|
|
533
|
-
analysis_name=analysis_name, as_df=True
|
|
899
|
+
analysis_name=analysis_name, as_df=True
|
|
534
900
|
)
|
|
535
901
|
|
|
536
902
|
# apply post processing
|
|
@@ -549,26 +915,198 @@ class SeerSDK:
|
|
|
549
915
|
|
|
550
916
|
return res_df.to_dict(orient="records") if not as_df else res_df
|
|
551
917
|
|
|
552
|
-
def
|
|
918
|
+
def find_samples(
|
|
553
919
|
self,
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
920
|
+
plate_id: str = None,
|
|
921
|
+
project_id: str = None,
|
|
922
|
+
project_name: str = None,
|
|
923
|
+
analysis_id: str = None,
|
|
924
|
+
analysis_name: str = None,
|
|
925
|
+
as_df: bool = False,
|
|
557
926
|
):
|
|
558
927
|
"""
|
|
559
|
-
|
|
560
|
-
[UNEXPOSED METHOD CALL]
|
|
561
|
-
****************
|
|
562
|
-
Get samples given a filter and project_id.
|
|
928
|
+
Fetches a list of samples. If no parameters are provided, returns all samples. 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.
|
|
563
929
|
|
|
564
930
|
Parameters
|
|
565
931
|
----------
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
932
|
+
plate_id : str, optional
|
|
933
|
+
ID of the plate for which samples are to be fetched, defaulted to None.
|
|
934
|
+
plate_name : str, optional
|
|
935
|
+
Name of the plate to be fetched, defaulted to None.
|
|
936
|
+
project_id : str, optional
|
|
937
|
+
ID of the project for which samples are to be fetched, defaulted to None.
|
|
938
|
+
project_name : str, optional
|
|
939
|
+
Name of the project to filter plates by, defaulted to None.
|
|
940
|
+
analysis_id : str, optional
|
|
941
|
+
ID of the analysis for which samples are to be fetched, defaulted to None.
|
|
942
|
+
analysis_name : str, optional
|
|
943
|
+
Name of the analysis for which samples are to be fetched, defaulted to None.
|
|
944
|
+
as_df: bool
|
|
945
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
946
|
+
|
|
947
|
+
Returns
|
|
948
|
+
-------
|
|
949
|
+
samples: list[dict] or DataFrame
|
|
950
|
+
List/DataFrame of samples for the authenticated user.
|
|
951
|
+
|
|
952
|
+
Examples
|
|
953
|
+
-------
|
|
954
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
955
|
+
>>> seer_sdk = SeerSDK()
|
|
956
|
+
|
|
957
|
+
>>> seer_sdk.find_samples(plate_id="7ec8cad0-15e0-11ee-bdf1-bbaa73585acf")
|
|
958
|
+
>>> [
|
|
959
|
+
{ "id": ... },
|
|
960
|
+
{ "id": ... },
|
|
961
|
+
...
|
|
962
|
+
]
|
|
963
|
+
|
|
964
|
+
>>> seer_sdk.find_samples(as_df=True)
|
|
965
|
+
>>> id ... control
|
|
966
|
+
0 812139c0-15e0-11ee-bdf1-bbaa73585acf ...
|
|
967
|
+
1 803e05b0-15e0-11ee-bdf1-bbaa73585acf ... MPE Control
|
|
968
|
+
2 a9b26a40-15da-11ee-bdf1-bbaa73585acf ...
|
|
969
|
+
3 a8fc87c0-15da-11ee-bdf1-bbaa73585acf ... MPE Control
|
|
970
|
+
4 8e322990-15da-11ee-bdf1-bbaa73585acf ...
|
|
971
|
+
... ... ... ...
|
|
972
|
+
3624 907e1f40-6621-11ea-96e3-d5a4dab4ebf6 ... C132
|
|
973
|
+
3625 53e59450-6621-11ea-96e3-d5a4dab4ebf6 ... C132
|
|
974
|
+
3626 5d11b030-6618-11ea-96e3-d5a4dab4ebf6 ... C132
|
|
975
|
+
3627 5bdf9270-6610-11ea-96e3-d5a4dab4ebf6 ... C132
|
|
976
|
+
3628 dd607ef0-654c-11ea-8eb2-25a1cfd1163c ... C132
|
|
977
|
+
"""
|
|
978
|
+
|
|
979
|
+
# Raise an error if none or more than one of the primary key parameters are passed in.
|
|
980
|
+
if (
|
|
981
|
+
sum(
|
|
982
|
+
[
|
|
983
|
+
True if x else False
|
|
984
|
+
for x in [plate_id, project_id, analysis_id, analysis_name]
|
|
985
|
+
]
|
|
986
|
+
)
|
|
987
|
+
> 1
|
|
988
|
+
):
|
|
989
|
+
raise ValueError(
|
|
990
|
+
"You must pass in no more than one of plate_id, project_id, analysis_id, analysis_name."
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
res = []
|
|
994
|
+
URL = f"{self._auth.url}api/v1/samples"
|
|
995
|
+
|
|
996
|
+
if project_name and not project_id:
|
|
997
|
+
projects = self.find_projects(project_name=project_name)
|
|
998
|
+
if not projects or len(projects) > 1:
|
|
999
|
+
raise ValueError(
|
|
1000
|
+
f"Project name '{project_name}' is invalid or ambiguous. Please specify a project_id."
|
|
1001
|
+
)
|
|
1002
|
+
project_id = projects[0]["id"]
|
|
1003
|
+
|
|
1004
|
+
sample_params = {"all": "true"}
|
|
1005
|
+
|
|
1006
|
+
if project_id or plate_id:
|
|
1007
|
+
with self._get_auth_session("findsamples") as s:
|
|
1008
|
+
if plate_id:
|
|
1009
|
+
search_name = "plate_id"
|
|
1010
|
+
search_value = plate_id
|
|
1011
|
+
sample_params["plateId"] = plate_id
|
|
1012
|
+
|
|
1013
|
+
else:
|
|
1014
|
+
search_name = "project_id"
|
|
1015
|
+
search_value = project_id
|
|
1016
|
+
sample_params["projectId"] = project_id
|
|
1017
|
+
|
|
1018
|
+
samples = s.get(URL, params=sample_params)
|
|
1019
|
+
if samples.status_code != 200:
|
|
1020
|
+
raise ValueError(
|
|
1021
|
+
f"Failed to fetch sample data for {search_name}: {search_value}."
|
|
1022
|
+
)
|
|
1023
|
+
res = samples.json()["data"]
|
|
1024
|
+
if not res:
|
|
1025
|
+
return (
|
|
1026
|
+
[] if not as_df else pd.DataFrame(columns=SAMPLE_COLUMNS)
|
|
1027
|
+
)
|
|
1028
|
+
res_df = dict_to_df(res)
|
|
1029
|
+
|
|
1030
|
+
# API returns empty strings if not a control, replace with None for filtering purposes
|
|
1031
|
+
res_df["control"] = res_df["control"].apply(
|
|
1032
|
+
lambda x: x if x else None
|
|
1033
|
+
)
|
|
1034
|
+
elif analysis_id or analysis_name:
|
|
1035
|
+
if analysis_id:
|
|
1036
|
+
res_df = self._get_analysis_samples(
|
|
1037
|
+
analysis_id=analysis_id, as_df=True
|
|
1038
|
+
)
|
|
1039
|
+
else:
|
|
1040
|
+
res_df = self._get_analysis_samples(
|
|
1041
|
+
analysis_name=analysis_name, as_df=True
|
|
1042
|
+
)
|
|
1043
|
+
else:
|
|
1044
|
+
with self._get_auth_session("findsamples") as s:
|
|
1045
|
+
samples = s.get(URL, params=sample_params)
|
|
1046
|
+
if samples.status_code != 200:
|
|
1047
|
+
raise ValueError(
|
|
1048
|
+
"Failed to fetch sample data for the authenticated user."
|
|
1049
|
+
)
|
|
1050
|
+
res = samples.json()["data"]
|
|
1051
|
+
res_df = dict_to_df(res)
|
|
1052
|
+
|
|
1053
|
+
if res_df.empty and as_df:
|
|
1054
|
+
return pd.DataFrame(columns=SAMPLE_COLUMNS)
|
|
1055
|
+
|
|
1056
|
+
plate_uuid_to_id = {
|
|
1057
|
+
x["plate_uuid"]: x["plate_id"] for x in self.find_plates()
|
|
1058
|
+
}
|
|
1059
|
+
# apply post processing
|
|
1060
|
+
if "tenant_id" in res_df.columns:
|
|
1061
|
+
res_df.drop(["tenant_id"], axis=1, inplace=True)
|
|
1062
|
+
|
|
1063
|
+
if "user_group" in res_df.columns:
|
|
1064
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
1065
|
+
res_df["space"] = res_df["user_group"].apply(
|
|
1066
|
+
lambda x: spaces.get(x, "General")
|
|
1067
|
+
)
|
|
1068
|
+
res_df.drop(["user_group"], axis=1, inplace=True)
|
|
1069
|
+
if "plate_id" in res_df.columns:
|
|
1070
|
+
res_df["plate_uuid"] = res_df["plate_id"]
|
|
1071
|
+
res_df["plate_id"] = res_df["plate_uuid"].apply(
|
|
1072
|
+
lambda x: plate_uuid_to_id.get(x, None)
|
|
1073
|
+
)
|
|
1074
|
+
|
|
1075
|
+
custom_columns = [
|
|
1076
|
+
x["field_name"] for x in self._get_sample_custom_fields()
|
|
1077
|
+
]
|
|
1078
|
+
res_df = res_df[
|
|
1079
|
+
[
|
|
1080
|
+
x
|
|
1081
|
+
for x in res_df.columns
|
|
1082
|
+
if not x.startswith("custom_") or x in custom_columns
|
|
1083
|
+
]
|
|
1084
|
+
]
|
|
1085
|
+
|
|
1086
|
+
if res_df.empty and as_df:
|
|
1087
|
+
return pd.DataFrame(columns=SAMPLE_COLUMNS)
|
|
1088
|
+
return res_df.to_dict(orient="records") if not as_df else res_df
|
|
1089
|
+
|
|
1090
|
+
def _filter_samples_metadata(
|
|
1091
|
+
self,
|
|
1092
|
+
project_id: str,
|
|
1093
|
+
filter: str,
|
|
1094
|
+
sample_ids: list = None,
|
|
1095
|
+
):
|
|
1096
|
+
"""
|
|
1097
|
+
****************
|
|
1098
|
+
[UNEXPOSED METHOD CALL]
|
|
1099
|
+
****************
|
|
1100
|
+
Get samples given a filter and project_id.
|
|
1101
|
+
|
|
1102
|
+
Parameters
|
|
1103
|
+
----------
|
|
1104
|
+
project_id : str
|
|
1105
|
+
The project id.
|
|
1106
|
+
filter : str
|
|
1107
|
+
The filter to be applied. Acceptable values are 'control' or 'sample'.
|
|
1108
|
+
sample_ids : list, optional
|
|
1109
|
+
List of user provided sample ids
|
|
572
1110
|
|
|
573
1111
|
Returns
|
|
574
1112
|
-------
|
|
@@ -579,7 +1117,7 @@ class SeerSDK:
|
|
|
579
1117
|
-------
|
|
580
1118
|
>>> from core import SeerSDK
|
|
581
1119
|
>>> seer_sdk = SeerSDK()
|
|
582
|
-
>>> seer_sdk.
|
|
1120
|
+
>>> seer_sdk._filter_samples_metadata("FILTER", "PROJECT_ID")
|
|
583
1121
|
>>> {
|
|
584
1122
|
"samples": [
|
|
585
1123
|
{
|
|
@@ -600,7 +1138,7 @@ class SeerSDK:
|
|
|
600
1138
|
"Invalid filter. Please choose between 'control' or 'sample'."
|
|
601
1139
|
)
|
|
602
1140
|
|
|
603
|
-
df = self.
|
|
1141
|
+
df = self.find_samples(project_id=project_id, as_df=True)
|
|
604
1142
|
|
|
605
1143
|
if filter == "control":
|
|
606
1144
|
df = df[~df["control"].isna()]
|
|
@@ -619,7 +1157,7 @@ class SeerSDK:
|
|
|
619
1157
|
"""
|
|
620
1158
|
URL = f"{self._auth.url}api/v1/samplefields"
|
|
621
1159
|
|
|
622
|
-
with self._get_auth_session() as s:
|
|
1160
|
+
with self._get_auth_session("getcustomcolumns") as s:
|
|
623
1161
|
|
|
624
1162
|
fields = s.get(URL)
|
|
625
1163
|
|
|
@@ -633,6 +1171,11 @@ class SeerSDK:
|
|
|
633
1171
|
del entry["tenant_id"]
|
|
634
1172
|
return res
|
|
635
1173
|
|
|
1174
|
+
@deprecation.deprecated(
|
|
1175
|
+
deprecated_in="1.1.0",
|
|
1176
|
+
removed_in="2.0.0",
|
|
1177
|
+
details="This method is deprecated and will be removed in a future release. Use `find_msruns` instead.",
|
|
1178
|
+
)
|
|
636
1179
|
def get_msruns(self, sample_ids: list, as_df: bool = False):
|
|
637
1180
|
"""
|
|
638
1181
|
Fetches MS data files for passed in `sample_ids` (provided they are valid and contain relevant files) for an authenticated user.
|
|
@@ -676,7 +1219,74 @@ class SeerSDK:
|
|
|
676
1219
|
res = []
|
|
677
1220
|
for sample_id in sample_ids:
|
|
678
1221
|
|
|
679
|
-
with self._get_auth_session() as s:
|
|
1222
|
+
with self._get_auth_session("getmsdatas") as s:
|
|
1223
|
+
|
|
1224
|
+
msdatas = s.post(URL, json={"sampleId": sample_id})
|
|
1225
|
+
|
|
1226
|
+
if msdatas.status_code != 200 or not msdatas.json()["data"]:
|
|
1227
|
+
raise ValueError(
|
|
1228
|
+
f"Failed to fetch MS data for sample ID={sample_id}."
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
res += [x for x in msdatas.json()["data"]]
|
|
1232
|
+
|
|
1233
|
+
for entry in res:
|
|
1234
|
+
if "tenant_id" in entry:
|
|
1235
|
+
del entry["tenant_id"]
|
|
1236
|
+
|
|
1237
|
+
if "raw_file_path" in entry:
|
|
1238
|
+
# Simple lambda function to find the third occurrence of '/' in the raw file path
|
|
1239
|
+
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
1240
|
+
# Slicing the string from the location
|
|
1241
|
+
entry["raw_file_path"] = entry["raw_file_path"][
|
|
1242
|
+
location(entry["raw_file_path"]) :
|
|
1243
|
+
]
|
|
1244
|
+
return res if not as_df else dict_to_df(res)
|
|
1245
|
+
|
|
1246
|
+
def find_msruns(self, sample_ids: list, as_df: bool = False):
|
|
1247
|
+
"""
|
|
1248
|
+
Fetches MS data files for passed in `sample_ids` (provided they are valid and contain relevant files) for an authenticated user.
|
|
1249
|
+
|
|
1250
|
+
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.
|
|
1251
|
+
|
|
1252
|
+
Parameters
|
|
1253
|
+
----------
|
|
1254
|
+
sample_ids : list
|
|
1255
|
+
List of unique sample IDs.
|
|
1256
|
+
as_df: bool
|
|
1257
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
1258
|
+
|
|
1259
|
+
Returns
|
|
1260
|
+
-------
|
|
1261
|
+
res: list[dict] or DataFrame
|
|
1262
|
+
List/DataFrame of plate objects for the authenticated user.
|
|
1263
|
+
|
|
1264
|
+
Examples
|
|
1265
|
+
-------
|
|
1266
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1267
|
+
>>> seer_sdk = SeerSDK()
|
|
1268
|
+
>>> sample_ids = ["812139c0-15e0-11ee-bdf1-bbaa73585acf", "803e05b0-15e0-11ee-bdf1-bbaa73585acf"]
|
|
1269
|
+
|
|
1270
|
+
>>> seer_sdk.find_msruns(sample_ids)
|
|
1271
|
+
>>> [
|
|
1272
|
+
{"id": "SAMPLE_ID_1_HERE" ... },
|
|
1273
|
+
{"id": "SAMPLE_ID_2_HERE" ... }
|
|
1274
|
+
]
|
|
1275
|
+
|
|
1276
|
+
>>> seer_sdk.find_msruns(sample_ids, as_df=True)
|
|
1277
|
+
>>> id ... gradient
|
|
1278
|
+
0 81c6a180-15e0-11ee-bdf1-bbaa73585acf ... None
|
|
1279
|
+
1 816a9ed0-15e0-11ee-bdf1-bbaa73585acf ... None
|
|
1280
|
+
|
|
1281
|
+
[2 rows x 26 columns]
|
|
1282
|
+
"""
|
|
1283
|
+
|
|
1284
|
+
URL = f"{self._auth.url}api/v1/msdatas/items"
|
|
1285
|
+
|
|
1286
|
+
res = []
|
|
1287
|
+
for sample_id in sample_ids:
|
|
1288
|
+
|
|
1289
|
+
with self._get_auth_session("findmsdatas") as s:
|
|
680
1290
|
|
|
681
1291
|
msdatas = s.post(URL, json={"sampleId": sample_id})
|
|
682
1292
|
|
|
@@ -687,6 +1297,7 @@ class SeerSDK:
|
|
|
687
1297
|
|
|
688
1298
|
res += [x for x in msdatas.json()["data"]]
|
|
689
1299
|
|
|
1300
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
690
1301
|
for entry in res:
|
|
691
1302
|
if "tenant_id" in entry:
|
|
692
1303
|
del entry["tenant_id"]
|
|
@@ -698,8 +1309,19 @@ class SeerSDK:
|
|
|
698
1309
|
entry["raw_file_path"] = entry["raw_file_path"][
|
|
699
1310
|
location(entry["raw_file_path"]) :
|
|
700
1311
|
]
|
|
1312
|
+
if "user_group" in entry:
|
|
1313
|
+
entry["space"] = spaces.get(entry["user_group"], "General")
|
|
1314
|
+
del entry["user_group"]
|
|
1315
|
+
|
|
1316
|
+
if not res and as_df:
|
|
1317
|
+
return pd.DataFrame(columns=MSRUN_COLUMNS)
|
|
701
1318
|
return res if not as_df else dict_to_df(res)
|
|
702
1319
|
|
|
1320
|
+
@deprecation.deprecated(
|
|
1321
|
+
deprecated_in="1.1.0",
|
|
1322
|
+
removed_in="2.0.0",
|
|
1323
|
+
details="This method is deprecated and will be removed in a future release. Use `find_analysis_protocols` instead.",
|
|
1324
|
+
)
|
|
703
1325
|
def get_analysis_protocols(
|
|
704
1326
|
self,
|
|
705
1327
|
analysis_protocol_name: str = None,
|
|
@@ -724,80 +1346,515 @@ class SeerSDK:
|
|
|
724
1346
|
protocols: list[dict]
|
|
725
1347
|
List of analysis protocol objects for the authenticated user.
|
|
726
1348
|
|
|
727
|
-
Examples
|
|
728
|
-
-------
|
|
729
|
-
>>> from seer_pas_sdk import SeerSDK
|
|
730
|
-
>>> seer_sdk = SeerSDK()
|
|
731
|
-
>>> seer_sdk.get_analysis_protocols()
|
|
732
|
-
>>> [
|
|
733
|
-
{ "id": ..., "analysis_protocol_name": ... },
|
|
734
|
-
{ "id": ..., "analysis_protocol_name": ... },
|
|
735
|
-
...
|
|
736
|
-
]
|
|
1349
|
+
Examples
|
|
1350
|
+
-------
|
|
1351
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1352
|
+
>>> seer_sdk = SeerSDK()
|
|
1353
|
+
>>> seer_sdk.get_analysis_protocols()
|
|
1354
|
+
>>> [
|
|
1355
|
+
{ "id": ..., "analysis_protocol_name": ... },
|
|
1356
|
+
{ "id": ..., "analysis_protocol_name": ... },
|
|
1357
|
+
...
|
|
1358
|
+
]
|
|
1359
|
+
|
|
1360
|
+
>>> seer_sdk.get_analysis_protocols(name="YOUR_ANALYSIS_PROTOCOL_NAME_HERE")
|
|
1361
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }]
|
|
1362
|
+
|
|
1363
|
+
>>> seer_sdk.get_analysis_protocols(id="YOUR_ANALYSIS_PROTOCOL_ID_HERE")
|
|
1364
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }]
|
|
1365
|
+
|
|
1366
|
+
>>> seer_sdk.get_analysis_protocols(id="YOUR_ANALYSIS_PROTOCOL_ID_HERE", name="YOUR_ANALYSIS_PROTOCOL_NAME_HERE")
|
|
1367
|
+
|
|
1368
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }] # in this case the id would supersede the inputted name.
|
|
1369
|
+
"""
|
|
1370
|
+
|
|
1371
|
+
URL = (
|
|
1372
|
+
f"{self._auth.url}api/v1/analysisProtocols"
|
|
1373
|
+
if not analysis_protocol_id
|
|
1374
|
+
else f"{self._auth.url}api/v1/analysisProtocols/{analysis_protocol_id}"
|
|
1375
|
+
)
|
|
1376
|
+
res = []
|
|
1377
|
+
params = {"all": "true"}
|
|
1378
|
+
|
|
1379
|
+
if analysis_protocol_name:
|
|
1380
|
+
params.update(
|
|
1381
|
+
{
|
|
1382
|
+
"searchFields": "analysis_protocol_name,offering_name",
|
|
1383
|
+
"searchItem": analysis_protocol_name,
|
|
1384
|
+
}
|
|
1385
|
+
)
|
|
1386
|
+
|
|
1387
|
+
with self._get_auth_session("getanalysisprotocols") as s:
|
|
1388
|
+
|
|
1389
|
+
protocols = s.get(URL, params=params)
|
|
1390
|
+
if protocols.status_code != 200:
|
|
1391
|
+
raise ValueError(
|
|
1392
|
+
"Invalid request. Please check your parameters."
|
|
1393
|
+
)
|
|
1394
|
+
if analysis_protocol_id:
|
|
1395
|
+
res = [protocols.json()]
|
|
1396
|
+
else:
|
|
1397
|
+
res = protocols.json()["data"]
|
|
1398
|
+
|
|
1399
|
+
for entry in range(len(res)):
|
|
1400
|
+
if "tenant_id" in res[entry]:
|
|
1401
|
+
del res[entry]["tenant_id"]
|
|
1402
|
+
|
|
1403
|
+
if "can_edit" in res[entry]:
|
|
1404
|
+
del res[entry]["can_edit"]
|
|
1405
|
+
|
|
1406
|
+
if "can_delete" in res[entry]:
|
|
1407
|
+
del res[entry]["can_delete"]
|
|
1408
|
+
|
|
1409
|
+
if "scope" in res[entry]:
|
|
1410
|
+
del res[entry]["scope"]
|
|
1411
|
+
|
|
1412
|
+
if "parameter_file_path" in res[entry]:
|
|
1413
|
+
# Simple lambda function to find the third occurrence of '/' in the raw file path
|
|
1414
|
+
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
1415
|
+
# Slicing the string from the location
|
|
1416
|
+
res[entry]["parameter_file_path"] = res[entry][
|
|
1417
|
+
"parameter_file_path"
|
|
1418
|
+
][location(res[entry]["parameter_file_path"]) :]
|
|
1419
|
+
|
|
1420
|
+
return res if not as_df else dict_to_df(res)
|
|
1421
|
+
|
|
1422
|
+
def get_analysis_protocol(
|
|
1423
|
+
self,
|
|
1424
|
+
analysis_protocol_id: str = None,
|
|
1425
|
+
analysis_protocol_name: str = None,
|
|
1426
|
+
):
|
|
1427
|
+
"""
|
|
1428
|
+
Fetches an analysis protocol.
|
|
1429
|
+
|
|
1430
|
+
Args:
|
|
1431
|
+
analysis_protocol_id (str, optional): id of the analysis protocol to be fetched. Defaults to None.
|
|
1432
|
+
analysis_protocol_name (str, optional): name of the analysis protocol to be fetched. Defaults to None.
|
|
1433
|
+
Returns:
|
|
1434
|
+
dict: Analysis protocol object
|
|
1435
|
+
Examples
|
|
1436
|
+
-------
|
|
1437
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1438
|
+
>>> seer_sdk = SeerSDK()
|
|
1439
|
+
>>> seer_sdk.get_analysis_protocol(analysis_protocol_id="YOUR_ANALYSIS_PROTOCOL_ID_HERE")
|
|
1440
|
+
>>> { "id": ..., "analysis_protocol_name": ... }
|
|
1441
|
+
>>> seer_sdk.get_analysis_protocol(analysis_protocol_name="YOUR_ANALYSIS_PROTOCOL_NAME_HERE")
|
|
1442
|
+
>>> { "id": ..., "analysis_protocol_name": ... }
|
|
1443
|
+
"""
|
|
1444
|
+
|
|
1445
|
+
if not (bool(analysis_protocol_id) ^ bool(analysis_protocol_name)):
|
|
1446
|
+
raise ValueError(
|
|
1447
|
+
"You must provide either analysis_protocol_id or analysis_protocol_name, but not both."
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
if analysis_protocol_id:
|
|
1451
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/{analysis_protocol_id}"
|
|
1452
|
+
with self._get_auth_session("getanalysisprotocol") as s:
|
|
1453
|
+
protocols = s.get(URL)
|
|
1454
|
+
if protocols.status_code != 200:
|
|
1455
|
+
raise ValueError(
|
|
1456
|
+
"Invalid request. Please check your parameters."
|
|
1457
|
+
)
|
|
1458
|
+
res = protocols.json()
|
|
1459
|
+
spaces = {
|
|
1460
|
+
x["id"]: x["usergroup_name"] for x in self.get_spaces()
|
|
1461
|
+
}
|
|
1462
|
+
if "tenant_id" in res:
|
|
1463
|
+
del res["tenant_id"]
|
|
1464
|
+
if "user_group" in res:
|
|
1465
|
+
res["space"] = spaces.get(res["user_group"], "General")
|
|
1466
|
+
del res["user_group"]
|
|
1467
|
+
try:
|
|
1468
|
+
res["fasta"] = ",".join(
|
|
1469
|
+
self._get_analysis_protocol_fasta_filenames(
|
|
1470
|
+
analysis_protocol_id=res["id"],
|
|
1471
|
+
analysis_protocol_engine=res["analysis_engine"],
|
|
1472
|
+
)
|
|
1473
|
+
)
|
|
1474
|
+
except:
|
|
1475
|
+
res["fasta"] = ""
|
|
1476
|
+
return res
|
|
1477
|
+
else:
|
|
1478
|
+
res = self.find_analysis_protocols(
|
|
1479
|
+
analysis_protocol_name=analysis_protocol_name
|
|
1480
|
+
)
|
|
1481
|
+
if not res:
|
|
1482
|
+
raise ValueError(
|
|
1483
|
+
f"No analysis protocol found with name '{analysis_protocol_name}'."
|
|
1484
|
+
)
|
|
1485
|
+
elif len(res) > 1:
|
|
1486
|
+
raise ValueError(
|
|
1487
|
+
f"Multiple analysis protocols found with name '{analysis_protocol_name}'. Please specify an analysis_protocol_id."
|
|
1488
|
+
)
|
|
1489
|
+
else:
|
|
1490
|
+
return res[0]
|
|
1491
|
+
|
|
1492
|
+
def find_analysis_protocols(
|
|
1493
|
+
self,
|
|
1494
|
+
analysis_protocol_name: str = None,
|
|
1495
|
+
analysis_protocol_id: str = None,
|
|
1496
|
+
as_df: bool = False,
|
|
1497
|
+
):
|
|
1498
|
+
"""
|
|
1499
|
+
Fetches a list of analysis protocols for the authenticated user. If no `analysis_protocol_id` is provided, returns all analysis protocols for the authenticated user. If `analysis_protocol_name` (and no `analysis_protocol_id`) is provided, returns the analysis protocol with the given name, provided it exists.
|
|
1500
|
+
|
|
1501
|
+
Parameters
|
|
1502
|
+
----------
|
|
1503
|
+
analysis_protocol_id : str, optional
|
|
1504
|
+
ID of the analysis protocol to be fetched, defaulted to None.
|
|
1505
|
+
|
|
1506
|
+
analysis_protocol_name : str, optional
|
|
1507
|
+
Name of the analysis protocol to be fetched, defaulted to None.
|
|
1508
|
+
|
|
1509
|
+
as_df : bool, optional
|
|
1510
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
1511
|
+
Returns
|
|
1512
|
+
-------
|
|
1513
|
+
protocols: list[dict]
|
|
1514
|
+
List of analysis protocol objects for the authenticated user.
|
|
1515
|
+
|
|
1516
|
+
Examples
|
|
1517
|
+
-------
|
|
1518
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1519
|
+
>>> seer_sdk = SeerSDK()
|
|
1520
|
+
>>> seer_sdk.find_analysis_protocols()
|
|
1521
|
+
>>> [
|
|
1522
|
+
{ "id": ..., "analysis_protocol_name": ... },
|
|
1523
|
+
{ "id": ..., "analysis_protocol_name": ... },
|
|
1524
|
+
...
|
|
1525
|
+
]
|
|
1526
|
+
|
|
1527
|
+
>>> seer_sdk.find_analysis_protocols(name="YOUR_ANALYSIS_PROTOCOL_NAME_HERE")
|
|
1528
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }]
|
|
1529
|
+
|
|
1530
|
+
>>> seer_sdk.find_analysis_protocols(id="YOUR_ANALYSIS_PROTOCOL_ID_HERE")
|
|
1531
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }]
|
|
1532
|
+
|
|
1533
|
+
>>> seer_sdk.find_analysis_protocols(id="YOUR_ANALYSIS_PROTOCOL_ID_HERE", name="YOUR_ANALYSIS_PROTOCOL_NAME_HERE")
|
|
1534
|
+
|
|
1535
|
+
>>> [{ "id": ..., "analysis_protocol_name": ... }] # in this case the id would supersede the inputted name.
|
|
1536
|
+
"""
|
|
1537
|
+
|
|
1538
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols"
|
|
1539
|
+
res = []
|
|
1540
|
+
params = {"all": "true"}
|
|
1541
|
+
|
|
1542
|
+
if analysis_protocol_name:
|
|
1543
|
+
params.update(
|
|
1544
|
+
{
|
|
1545
|
+
"searchFields": "analysis_protocol_name,offering_name",
|
|
1546
|
+
"searchItem": analysis_protocol_name,
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
elif analysis_protocol_id:
|
|
1550
|
+
params.update(
|
|
1551
|
+
{"searchFields": "id", "searchItem": analysis_protocol_id}
|
|
1552
|
+
)
|
|
1553
|
+
|
|
1554
|
+
with self._get_auth_session("findanalysisprotocols") as s:
|
|
1555
|
+
|
|
1556
|
+
protocols = s.get(URL, params=params)
|
|
1557
|
+
if protocols.status_code != 200:
|
|
1558
|
+
raise ValueError(
|
|
1559
|
+
"Invalid request. Please check your parameters."
|
|
1560
|
+
)
|
|
1561
|
+
res = protocols.json()["data"]
|
|
1562
|
+
|
|
1563
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
1564
|
+
for entry in range(len(res)):
|
|
1565
|
+
if "tenant_id" in res[entry]:
|
|
1566
|
+
del res[entry]["tenant_id"]
|
|
1567
|
+
|
|
1568
|
+
if "can_edit" in res[entry]:
|
|
1569
|
+
del res[entry]["can_edit"]
|
|
1570
|
+
|
|
1571
|
+
if "can_delete" in res[entry]:
|
|
1572
|
+
del res[entry]["can_delete"]
|
|
1573
|
+
|
|
1574
|
+
if "scope" in res[entry]:
|
|
1575
|
+
del res[entry]["scope"]
|
|
1576
|
+
|
|
1577
|
+
if "parameter_file_path" in res[entry]:
|
|
1578
|
+
# Simple lambda function to find the third occurrence of '/' in the raw file path
|
|
1579
|
+
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
1580
|
+
# Slicing the string from the location
|
|
1581
|
+
res[entry]["parameter_file_path"] = res[entry][
|
|
1582
|
+
"parameter_file_path"
|
|
1583
|
+
][location(res[entry]["parameter_file_path"]) :]
|
|
1584
|
+
if "user_group" in res[entry]:
|
|
1585
|
+
res[entry]["space"] = spaces.get(
|
|
1586
|
+
res[entry]["user_group"], "General"
|
|
1587
|
+
)
|
|
1588
|
+
del res[entry]["user_group"]
|
|
1589
|
+
|
|
1590
|
+
try:
|
|
1591
|
+
res[entry]["fasta"] = ",".join(
|
|
1592
|
+
self._get_analysis_protocol_fasta_filenames(
|
|
1593
|
+
analysis_protocol_id=res[entry]["id"],
|
|
1594
|
+
analysis_protocol_engine=res[entry].get(
|
|
1595
|
+
"analysis_engine", None
|
|
1596
|
+
),
|
|
1597
|
+
)
|
|
1598
|
+
)
|
|
1599
|
+
except:
|
|
1600
|
+
res[entry]["fasta"] = ""
|
|
1601
|
+
|
|
1602
|
+
if not res and as_df:
|
|
1603
|
+
return pd.DataFrame(columns=ANALYSIS_PROTOCOL_COLUMNS)
|
|
1604
|
+
return res if not as_df else dict_to_df(res)
|
|
1605
|
+
|
|
1606
|
+
@deprecation.deprecated(
|
|
1607
|
+
deprecated_in="1.1.0",
|
|
1608
|
+
removed_in="2.0.0",
|
|
1609
|
+
details="This method is deprecated and will be removed in a future release. Use `find_analyses` instead.",
|
|
1610
|
+
)
|
|
1611
|
+
def get_analyses(
|
|
1612
|
+
self,
|
|
1613
|
+
analysis_id: str = None,
|
|
1614
|
+
folder_id: str = None,
|
|
1615
|
+
show_folders: bool = True,
|
|
1616
|
+
analysis_only: bool = True,
|
|
1617
|
+
project_id: str = None,
|
|
1618
|
+
plate_name: str = None,
|
|
1619
|
+
as_df=False,
|
|
1620
|
+
**kwargs,
|
|
1621
|
+
):
|
|
1622
|
+
"""
|
|
1623
|
+
Returns a list of analyses objects for the authenticated user. If no id is provided, returns all analyses for the authenticated user.
|
|
1624
|
+
Search parameters may be passed in as keyword arguments to filter the results. Acceptable values are 'analysis_name', 'folder_name', 'description', 'notes', or 'number_msdatafile'.
|
|
1625
|
+
Only search on a single field is supported.
|
|
1626
|
+
|
|
1627
|
+
Parameters
|
|
1628
|
+
----------
|
|
1629
|
+
analysis_id : str, optional
|
|
1630
|
+
ID of the analysis to be fetched, defaulted to None.
|
|
1631
|
+
|
|
1632
|
+
folder_id : str, optional
|
|
1633
|
+
ID of the folder to be fetched, defaulted to None.
|
|
1634
|
+
|
|
1635
|
+
show_folders : bool, optional
|
|
1636
|
+
Mark True if folder contents are to be returned in the response, i.e. recursive search, defaulted to True.
|
|
1637
|
+
Will be disabled if an analysis id is provided.
|
|
1638
|
+
|
|
1639
|
+
analysis_only : bool, optional
|
|
1640
|
+
Mark True if only analyses objects are to be returned in the response, defaulted to True.
|
|
1641
|
+
If marked false, folder objects will also be included in the response.
|
|
1642
|
+
|
|
1643
|
+
project_id : str, optional
|
|
1644
|
+
ID of the project to be fetched, defaulted to None.
|
|
1645
|
+
|
|
1646
|
+
plate_name : str, optional
|
|
1647
|
+
Name of the plate to be fetched, defaulted to None.
|
|
1648
|
+
|
|
1649
|
+
as_df : bool, optional
|
|
1650
|
+
whether the result should be converted to a DataFrame, defaulted to False.
|
|
1651
|
+
|
|
1652
|
+
**kwargs : dict, optional
|
|
1653
|
+
Search keyword parameters to be passed in. Acceptable values are 'analysis_name', 'folder_name', 'analysis_protocol_name', 'description', 'notes', or 'number_msdatafile'.
|
|
1654
|
+
|
|
1655
|
+
Returns
|
|
1656
|
+
-------
|
|
1657
|
+
analyses: list[dict]
|
|
1658
|
+
Contains a list of analyses objects for the authenticated user.
|
|
1659
|
+
|
|
1660
|
+
Examples
|
|
1661
|
+
-------
|
|
1662
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1663
|
+
>>> seer_sdk = SeerSDK()
|
|
1664
|
+
>>> seer_sdk.get_analyses()
|
|
1665
|
+
>>> [
|
|
1666
|
+
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
1667
|
+
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
1668
|
+
{id: "YOUR_ANALYSIS_ID_HERE", ...}
|
|
1669
|
+
]
|
|
1670
|
+
|
|
1671
|
+
>>> seer_sdk.get_analyses("YOUR_ANALYSIS_ID_HERE")
|
|
1672
|
+
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
1673
|
+
|
|
1674
|
+
>>> seer_sdk.get_analyses(folder_name="YOUR_FOLDER_NAME_HERE")
|
|
1675
|
+
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
1676
|
+
|
|
1677
|
+
>>> seer_sdk.get_analyses(analysis_name="YOUR_ANALYSIS")
|
|
1678
|
+
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
1679
|
+
|
|
1680
|
+
>>> seer_sdk.get_analyses(description="YOUR_DESCRIPTION")
|
|
1681
|
+
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
1682
|
+
"""
|
|
1683
|
+
|
|
1684
|
+
URL = f"{self._auth.url}api/v1/analyses"
|
|
1685
|
+
res = []
|
|
1686
|
+
|
|
1687
|
+
search_field = None
|
|
1688
|
+
search_item = None
|
|
1689
|
+
if kwargs:
|
|
1690
|
+
if len(kwargs.keys()) > 1:
|
|
1691
|
+
raise ValueError("Please include only one search parameter.")
|
|
1692
|
+
search_field = list(kwargs.keys())[0]
|
|
1693
|
+
search_item = kwargs[search_field]
|
|
1694
|
+
|
|
1695
|
+
if not search_item:
|
|
1696
|
+
raise ValueError(
|
|
1697
|
+
f"Please provide a non null value for {search_field}"
|
|
1698
|
+
)
|
|
1699
|
+
|
|
1700
|
+
if search_field and search_field not in [
|
|
1701
|
+
"analysis_name",
|
|
1702
|
+
"folder_name",
|
|
1703
|
+
"analysis_protocol_name",
|
|
1704
|
+
"description",
|
|
1705
|
+
"notes",
|
|
1706
|
+
"number_msdatafile",
|
|
1707
|
+
]:
|
|
1708
|
+
raise ValueError(
|
|
1709
|
+
"Invalid search field. Please choose between 'analysis_name', 'folder_name', 'analysis_protocol_name', 'description', 'notes', or 'number_msdatafile'."
|
|
1710
|
+
)
|
|
1711
|
+
|
|
1712
|
+
with self._get_auth_session("getanalyses") as s:
|
|
737
1713
|
|
|
738
|
-
|
|
739
|
-
|
|
1714
|
+
params = {"all": "true"}
|
|
1715
|
+
if folder_id:
|
|
1716
|
+
params["folder"] = folder_id
|
|
740
1717
|
|
|
741
|
-
|
|
742
|
-
|
|
1718
|
+
if search_field:
|
|
1719
|
+
params["searchFields"] = search_field
|
|
1720
|
+
params["searchItem"] = search_item
|
|
1721
|
+
del params["all"]
|
|
743
1722
|
|
|
744
|
-
|
|
1723
|
+
if search_field == "folder_name":
|
|
1724
|
+
params["searchFields"] = "analysis_name"
|
|
745
1725
|
|
|
746
|
-
|
|
747
|
-
|
|
1726
|
+
if project_id:
|
|
1727
|
+
params["projectId"] = project_id
|
|
748
1728
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if not analysis_protocol_id
|
|
752
|
-
else f"{self._auth.url}api/v1/analysisProtocols/{analysis_protocol_id}"
|
|
753
|
-
)
|
|
754
|
-
res = []
|
|
755
|
-
params = {"all": "true"}
|
|
1729
|
+
if plate_name:
|
|
1730
|
+
params["plateName"] = plate_name
|
|
756
1731
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
{
|
|
760
|
-
"searchFields": "analysis_protocol_name,offering_name",
|
|
761
|
-
"searchItem": analysis_protocol_name,
|
|
762
|
-
}
|
|
1732
|
+
analyses = s.get(
|
|
1733
|
+
f"{URL}/{analysis_id}" if analysis_id else URL, params=params
|
|
763
1734
|
)
|
|
764
1735
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
protocols = s.get(URL, params=params)
|
|
768
|
-
if protocols.status_code != 200:
|
|
1736
|
+
if analyses.status_code != 200:
|
|
769
1737
|
raise ValueError(
|
|
770
1738
|
"Invalid request. Please check your parameters."
|
|
771
1739
|
)
|
|
772
|
-
if
|
|
773
|
-
res =
|
|
1740
|
+
if not analysis_id:
|
|
1741
|
+
res = analyses.json()["data"]
|
|
1742
|
+
|
|
774
1743
|
else:
|
|
775
|
-
res =
|
|
1744
|
+
res = [analyses.json()["analysis"]]
|
|
776
1745
|
|
|
1746
|
+
folders = []
|
|
777
1747
|
for entry in range(len(res)):
|
|
778
1748
|
if "tenant_id" in res[entry]:
|
|
779
1749
|
del res[entry]["tenant_id"]
|
|
780
1750
|
|
|
781
|
-
if "can_edit" in res[entry]:
|
|
782
|
-
del res[entry]["can_edit"]
|
|
783
|
-
|
|
784
|
-
if "can_delete" in res[entry]:
|
|
785
|
-
del res[entry]["can_delete"]
|
|
786
|
-
|
|
787
|
-
if "scope" in res[entry]:
|
|
788
|
-
del res[entry]["scope"]
|
|
789
|
-
|
|
790
1751
|
if "parameter_file_path" in res[entry]:
|
|
791
1752
|
# Simple lambda function to find the third occurrence of '/' in the raw file path
|
|
792
1753
|
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
1754
|
+
|
|
793
1755
|
# Slicing the string from the location
|
|
794
1756
|
res[entry]["parameter_file_path"] = res[entry][
|
|
795
1757
|
"parameter_file_path"
|
|
796
1758
|
][location(res[entry]["parameter_file_path"]) :]
|
|
797
1759
|
|
|
1760
|
+
if (
|
|
1761
|
+
show_folders
|
|
1762
|
+
and not analysis_id
|
|
1763
|
+
and res[entry]["is_folder"]
|
|
1764
|
+
):
|
|
1765
|
+
folders.append(res[entry]["id"])
|
|
1766
|
+
|
|
1767
|
+
# recursive solution to get analyses in folders
|
|
1768
|
+
for folder in folders:
|
|
1769
|
+
res += self.get_analyses(folder_id=folder)
|
|
1770
|
+
|
|
1771
|
+
if analysis_only:
|
|
1772
|
+
res = [
|
|
1773
|
+
analysis for analysis in res if not analysis["is_folder"]
|
|
1774
|
+
]
|
|
798
1775
|
return res if not as_df else dict_to_df(res)
|
|
799
1776
|
|
|
800
|
-
def
|
|
1777
|
+
def get_analysis(
|
|
1778
|
+
self,
|
|
1779
|
+
analysis_id: str = None,
|
|
1780
|
+
analysis_name: str = None,
|
|
1781
|
+
):
|
|
1782
|
+
"""Fetches an analysis.
|
|
1783
|
+
|
|
1784
|
+
Args:
|
|
1785
|
+
analysis_id (str, optional): id of the analysis to be fetched. Defaults to None.
|
|
1786
|
+
analysis_name (str, optional): name of the analysis to be fetched. Defaults to None.
|
|
1787
|
+
|
|
1788
|
+
Returns:
|
|
1789
|
+
analysis : dict
|
|
1790
|
+
Analysis object
|
|
1791
|
+
Examples
|
|
1792
|
+
-------
|
|
1793
|
+
>>> from seer_pas_sdk import SeerSDK
|
|
1794
|
+
>>> seer_sdk = SeerSDK()
|
|
1795
|
+
>>> seer_sdk.get_analysis(analysis_id="YOUR_ANALYSIS_ID_HERE")
|
|
1796
|
+
>>> { "id": ..., "analysis_name": ... }
|
|
1797
|
+
|
|
1798
|
+
"""
|
|
1799
|
+
if not (bool(analysis_id) ^ bool(analysis_name)):
|
|
1800
|
+
raise ValueError(
|
|
1801
|
+
"You must provide either analysis_id or analysis_name, but not both."
|
|
1802
|
+
)
|
|
1803
|
+
|
|
1804
|
+
if analysis_id:
|
|
1805
|
+
URL = f"{self._auth.url}api/v1/analyses/{analysis_id}"
|
|
1806
|
+
with self._get_auth_session("getanalysis") as s:
|
|
1807
|
+
analysis = s.get(URL)
|
|
1808
|
+
if analysis.status_code != 200:
|
|
1809
|
+
raise ValueError(
|
|
1810
|
+
"Invalid request. Please check your parameters."
|
|
1811
|
+
)
|
|
1812
|
+
res = analysis.json()["analysis"]
|
|
1813
|
+
spaces = {
|
|
1814
|
+
x["id"]: x["usergroup_name"] for x in self.get_spaces()
|
|
1815
|
+
}
|
|
1816
|
+
if "tenant_id" in res:
|
|
1817
|
+
del res["tenant_id"]
|
|
1818
|
+
if "user_group" in res:
|
|
1819
|
+
res["space"] = spaces.get(res["user_group"], "General")
|
|
1820
|
+
del res["user_group"]
|
|
1821
|
+
if not res.get("is_folder") and res.get(
|
|
1822
|
+
"analysis_protocol_id"
|
|
1823
|
+
):
|
|
1824
|
+
analysis_protocol = self.get_analysis_protocol(
|
|
1825
|
+
analysis_protocol_id=res.get("analysis_protocol_id")
|
|
1826
|
+
)
|
|
1827
|
+
try:
|
|
1828
|
+
res["fasta"] = ",".join(
|
|
1829
|
+
self._get_analysis_protocol_fasta_filenames(
|
|
1830
|
+
analysis_protocol_id=res.get(
|
|
1831
|
+
"analysis_protocol_id"
|
|
1832
|
+
),
|
|
1833
|
+
analysis_protocol_engine=analysis_protocol.get(
|
|
1834
|
+
"analysis_engine", None
|
|
1835
|
+
),
|
|
1836
|
+
)
|
|
1837
|
+
)
|
|
1838
|
+
except Exception as e:
|
|
1839
|
+
print("Warning: Could not fetch fasta files.")
|
|
1840
|
+
res["fasta"] = None
|
|
1841
|
+
else:
|
|
1842
|
+
res["fasta"] = None
|
|
1843
|
+
return res
|
|
1844
|
+
else:
|
|
1845
|
+
res = self.find_analyses(analysis_name=analysis_name)
|
|
1846
|
+
if not res:
|
|
1847
|
+
raise ValueError(
|
|
1848
|
+
f"No analysis found with name '{analysis_name}'."
|
|
1849
|
+
)
|
|
1850
|
+
elif len(res) > 1:
|
|
1851
|
+
raise ValueError(
|
|
1852
|
+
f"Multiple analyses found with name '{analysis_name}'. Please specify an analysis_id."
|
|
1853
|
+
)
|
|
1854
|
+
else:
|
|
1855
|
+
return res[0]
|
|
1856
|
+
|
|
1857
|
+
def find_analyses(
|
|
801
1858
|
self,
|
|
802
1859
|
analysis_id: str = None,
|
|
803
1860
|
folder_id: str = None,
|
|
@@ -850,23 +1907,23 @@ class SeerSDK:
|
|
|
850
1907
|
-------
|
|
851
1908
|
>>> from seer_pas_sdk import SeerSDK
|
|
852
1909
|
>>> seer_sdk = SeerSDK()
|
|
853
|
-
>>> seer_sdk.
|
|
1910
|
+
>>> seer_sdk.find_analyses()
|
|
854
1911
|
>>> [
|
|
855
1912
|
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
856
1913
|
{id: "YOUR_ANALYSIS_ID_HERE", ...},
|
|
857
1914
|
{id: "YOUR_ANALYSIS_ID_HERE", ...}
|
|
858
1915
|
]
|
|
859
1916
|
|
|
860
|
-
>>> seer_sdk.
|
|
1917
|
+
>>> seer_sdk.find_analyses("YOUR_ANALYSIS_ID_HERE")
|
|
861
1918
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
862
1919
|
|
|
863
|
-
>>> seer_sdk.
|
|
1920
|
+
>>> seer_sdk.find_analyses(folder_name="YOUR_FOLDER_NAME_HERE")
|
|
864
1921
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
865
1922
|
|
|
866
|
-
>>> seer_sdk.
|
|
1923
|
+
>>> seer_sdk.find_analyses(analysis_name="YOUR_ANALYSIS")
|
|
867
1924
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
868
1925
|
|
|
869
|
-
>>> seer_sdk.
|
|
1926
|
+
>>> seer_sdk.find_analyses(description="YOUR_DESCRIPTION")
|
|
870
1927
|
>>> [{ id: "YOUR_ANALYSIS_ID_HERE", ...}]
|
|
871
1928
|
"""
|
|
872
1929
|
|
|
@@ -898,7 +1955,13 @@ class SeerSDK:
|
|
|
898
1955
|
"Invalid search field. Please choose between 'analysis_name', 'folder_name', 'analysis_protocol_name', 'description', 'notes', or 'number_msdatafile'."
|
|
899
1956
|
)
|
|
900
1957
|
|
|
901
|
-
|
|
1958
|
+
if analysis_id:
|
|
1959
|
+
try:
|
|
1960
|
+
return [self.get_analysis(analysis_id=analysis_id)]
|
|
1961
|
+
except:
|
|
1962
|
+
return []
|
|
1963
|
+
|
|
1964
|
+
with self._get_auth_session("findanalyses") as s:
|
|
902
1965
|
|
|
903
1966
|
params = {"all": "true"}
|
|
904
1967
|
if folder_id:
|
|
@@ -918,30 +1981,25 @@ class SeerSDK:
|
|
|
918
1981
|
if plate_name:
|
|
919
1982
|
params["plateName"] = plate_name
|
|
920
1983
|
|
|
921
|
-
analyses = s.get(
|
|
922
|
-
f"{URL}/{analysis_id}" if analysis_id else URL, params=params
|
|
923
|
-
)
|
|
1984
|
+
analyses = s.get(URL, params=params)
|
|
924
1985
|
|
|
925
1986
|
if analyses.status_code != 200:
|
|
926
1987
|
raise ValueError(
|
|
927
1988
|
"Invalid request. Please check your parameters."
|
|
928
1989
|
)
|
|
929
|
-
|
|
930
|
-
res = analyses.json()["data"]
|
|
931
|
-
|
|
932
|
-
else:
|
|
933
|
-
res = [analyses.json()["analysis"]]
|
|
1990
|
+
res = analyses.json()["data"]
|
|
934
1991
|
|
|
935
1992
|
folders = []
|
|
1993
|
+
spaces = {x["id"]: x["usergroup_name"] for x in self.get_spaces()}
|
|
1994
|
+
protocol_to_engine_map = dict()
|
|
936
1995
|
for entry in range(len(res)):
|
|
937
1996
|
if "tenant_id" in res[entry]:
|
|
938
1997
|
del res[entry]["tenant_id"]
|
|
939
1998
|
|
|
940
1999
|
if "parameter_file_path" in res[entry]:
|
|
941
|
-
#
|
|
2000
|
+
# Trim parameter file path to user visible attributes
|
|
942
2001
|
location = lambda s: len(s) - len(s.split("/", 3)[-1])
|
|
943
2002
|
|
|
944
|
-
# Slicing the string from the location
|
|
945
2003
|
res[entry]["parameter_file_path"] = res[entry][
|
|
946
2004
|
"parameter_file_path"
|
|
947
2005
|
][location(res[entry]["parameter_file_path"]) :]
|
|
@@ -953,17 +2011,65 @@ class SeerSDK:
|
|
|
953
2011
|
):
|
|
954
2012
|
folders.append(res[entry]["id"])
|
|
955
2013
|
|
|
2014
|
+
if "user_group" in res[entry]:
|
|
2015
|
+
res[entry]["space"] = spaces.get(
|
|
2016
|
+
res[entry]["user_group"], "General"
|
|
2017
|
+
)
|
|
2018
|
+
del res[entry]["user_group"]
|
|
2019
|
+
|
|
2020
|
+
if (not res[entry].get("is_folder")) and res[entry].get(
|
|
2021
|
+
"analysis_protocol_id"
|
|
2022
|
+
):
|
|
2023
|
+
if (
|
|
2024
|
+
res[entry]["analysis_protocol_id"]
|
|
2025
|
+
in protocol_to_engine_map
|
|
2026
|
+
):
|
|
2027
|
+
analysis_protocol_engine = protocol_to_engine_map[
|
|
2028
|
+
res[entry]["analysis_protocol_id"]
|
|
2029
|
+
]
|
|
2030
|
+
else:
|
|
2031
|
+
try:
|
|
2032
|
+
analysis_protocol = self.get_analysis_protocol(
|
|
2033
|
+
analysis_protocol_id=res[entry].get(
|
|
2034
|
+
"analysis_protocol_id"
|
|
2035
|
+
)
|
|
2036
|
+
)
|
|
2037
|
+
analysis_protocol_engine = analysis_protocol.get(
|
|
2038
|
+
"analysis_engine", None
|
|
2039
|
+
)
|
|
2040
|
+
protocol_to_engine_map[
|
|
2041
|
+
res[entry]["analysis_protocol_id"]
|
|
2042
|
+
] = analysis_protocol_engine
|
|
2043
|
+
except:
|
|
2044
|
+
analysis_protocol_engine = None
|
|
2045
|
+
try:
|
|
2046
|
+
res[entry]["fasta"] = ",".join(
|
|
2047
|
+
self._get_analysis_protocol_fasta_filenames(
|
|
2048
|
+
res[entry]["analysis_protocol_id"],
|
|
2049
|
+
analysis_protocol_engine=analysis_protocol_engine,
|
|
2050
|
+
)
|
|
2051
|
+
)
|
|
2052
|
+
except:
|
|
2053
|
+
print(
|
|
2054
|
+
f"Warning: Could not fetch fasta files for analysis {res[entry].get('analysis_name')}."
|
|
2055
|
+
)
|
|
2056
|
+
res[entry]["fasta"] = None
|
|
2057
|
+
else:
|
|
2058
|
+
res[entry]["fasta"] = None
|
|
2059
|
+
|
|
956
2060
|
# recursive solution to get analyses in folders
|
|
957
2061
|
for folder in folders:
|
|
958
|
-
res += self.
|
|
2062
|
+
res += self.find_analyses(folder_id=folder)
|
|
959
2063
|
|
|
960
2064
|
if analysis_only:
|
|
961
2065
|
res = [
|
|
962
2066
|
analysis for analysis in res if not analysis["is_folder"]
|
|
963
2067
|
]
|
|
2068
|
+
if not res and as_df:
|
|
2069
|
+
return pd.DataFrame(columns=ANALYSIS_COLUMNS)
|
|
964
2070
|
return res if not as_df else dict_to_df(res)
|
|
965
2071
|
|
|
966
|
-
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="
|
|
2072
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="2.0.0")
|
|
967
2073
|
def get_analysis_result_protein_data(
|
|
968
2074
|
self, analysis_id: str, link: bool = False, pg: str = None
|
|
969
2075
|
):
|
|
@@ -981,7 +2087,7 @@ class SeerSDK:
|
|
|
981
2087
|
Protein group ID to filter dataframe results. Defaults to None.
|
|
982
2088
|
|
|
983
2089
|
"""
|
|
984
|
-
with self._get_auth_session() as s:
|
|
2090
|
+
with self._get_auth_session("getanalysisresultprotein") as s:
|
|
985
2091
|
URL = f"{self._auth.url}api/v1/data"
|
|
986
2092
|
response = s.get(
|
|
987
2093
|
f"{URL}/protein?analysisId={analysis_id}&retry=false"
|
|
@@ -1013,16 +2119,18 @@ class SeerSDK:
|
|
|
1013
2119
|
else:
|
|
1014
2120
|
if not pg:
|
|
1015
2121
|
return {
|
|
1016
|
-
"protein_np":
|
|
1017
|
-
|
|
2122
|
+
"protein_np": download_df(
|
|
2123
|
+
protein_data["npLink"]["url"]
|
|
2124
|
+
),
|
|
2125
|
+
"protein_panel": download_df(
|
|
1018
2126
|
protein_data["panelLink"]["url"]
|
|
1019
2127
|
),
|
|
1020
2128
|
}
|
|
1021
2129
|
else:
|
|
1022
|
-
protein_np =
|
|
2130
|
+
protein_np = download_df(
|
|
1023
2131
|
protein_data["npLink"]["url"]
|
|
1024
2132
|
).query(f"`Protein Group` == '{pg}'")
|
|
1025
|
-
protein_panel =
|
|
2133
|
+
protein_panel = download_df(
|
|
1026
2134
|
protein_data["panelLink"]["url"]
|
|
1027
2135
|
).query(f"`Protein Group` == '{pg}'")
|
|
1028
2136
|
|
|
@@ -1036,7 +2144,7 @@ class SeerSDK:
|
|
|
1036
2144
|
"protein_panel": protein_panel,
|
|
1037
2145
|
}
|
|
1038
2146
|
|
|
1039
|
-
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="
|
|
2147
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="2.0.0")
|
|
1040
2148
|
def get_analysis_result_peptide_data(
|
|
1041
2149
|
self, analysis_id: str, link: bool = False, peptide: str = None
|
|
1042
2150
|
):
|
|
@@ -1057,7 +2165,7 @@ class SeerSDK:
|
|
|
1057
2165
|
|
|
1058
2166
|
"""
|
|
1059
2167
|
|
|
1060
|
-
with self._get_auth_session() as s:
|
|
2168
|
+
with self._get_auth_session("getanalysisresultpeptide") as s:
|
|
1061
2169
|
URL = f"{self._auth.url}api/v1/data"
|
|
1062
2170
|
response = s.get(
|
|
1063
2171
|
f"{URL}/peptide?analysisId={analysis_id}&retry=false"
|
|
@@ -1089,16 +2197,18 @@ class SeerSDK:
|
|
|
1089
2197
|
else:
|
|
1090
2198
|
if not peptide:
|
|
1091
2199
|
return {
|
|
1092
|
-
"peptide_np":
|
|
1093
|
-
|
|
2200
|
+
"peptide_np": download_df(
|
|
2201
|
+
peptide_data["npLink"]["url"]
|
|
2202
|
+
),
|
|
2203
|
+
"peptide_panel": download_df(
|
|
1094
2204
|
peptide_data["panelLink"]["url"]
|
|
1095
2205
|
),
|
|
1096
2206
|
}
|
|
1097
2207
|
else:
|
|
1098
|
-
peptide_np =
|
|
2208
|
+
peptide_np = download_df(
|
|
1099
2209
|
peptide_data["npLink"]["url"]
|
|
1100
2210
|
).query(f"Peptide == '{peptide}'")
|
|
1101
|
-
peptide_panel =
|
|
2211
|
+
peptide_panel = download_df(
|
|
1102
2212
|
peptide_data["panelLink"]["url"]
|
|
1103
2213
|
).query(f"Peptide == '{peptide}'")
|
|
1104
2214
|
|
|
@@ -1121,7 +2231,7 @@ class SeerSDK:
|
|
|
1121
2231
|
analysis_id : str
|
|
1122
2232
|
ID of the analysis for which the data is to be fetched.
|
|
1123
2233
|
"""
|
|
1124
|
-
with self._get_auth_session() as s:
|
|
2234
|
+
with self._get_auth_session("getsearchresultprotein") as s:
|
|
1125
2235
|
URL = f"{self._auth.url}api/v1/data"
|
|
1126
2236
|
response = s.get(
|
|
1127
2237
|
f"{URL}/protein?analysisId={analysis_id}&retry=false"
|
|
@@ -1167,7 +2277,7 @@ class SeerSDK:
|
|
|
1167
2277
|
|
|
1168
2278
|
"""
|
|
1169
2279
|
|
|
1170
|
-
with self._get_auth_session() as s:
|
|
2280
|
+
with self._get_auth_session("getsearchresultpeptide") as s:
|
|
1171
2281
|
URL = f"{self._auth.url}api/v1/data"
|
|
1172
2282
|
response = s.get(
|
|
1173
2283
|
f"{URL}/peptide?analysisId={analysis_id}&retry=false"
|
|
@@ -1220,7 +2330,7 @@ class SeerSDK:
|
|
|
1220
2330
|
List of files associated with the analysis.
|
|
1221
2331
|
"""
|
|
1222
2332
|
try:
|
|
1223
|
-
analysis_metadata = self.
|
|
2333
|
+
analysis_metadata = self.find_analyses(analysis_id)[0]
|
|
1224
2334
|
except (IndexError, ServerError):
|
|
1225
2335
|
raise ValueError("Invalid analysis ID.")
|
|
1226
2336
|
except:
|
|
@@ -1233,7 +2343,7 @@ class SeerSDK:
|
|
|
1233
2343
|
if folder:
|
|
1234
2344
|
params["folderKey"] = folder
|
|
1235
2345
|
|
|
1236
|
-
with self._get_auth_session() as s:
|
|
2346
|
+
with self._get_auth_session("listsearchresultfiles") as s:
|
|
1237
2347
|
response = s.get(
|
|
1238
2348
|
f"{self._auth.url}api/v2/analysisResultFiles/{analysis_id}",
|
|
1239
2349
|
params=params,
|
|
@@ -1284,47 +2394,54 @@ class SeerSDK:
|
|
|
1284
2394
|
|
|
1285
2395
|
if analyte_type not in ["protein", "peptide", "precursor"]:
|
|
1286
2396
|
raise ValueError(
|
|
1287
|
-
"
|
|
2397
|
+
"Unknown analyte_type = '{analyte_type}'. Supported types are 'protein', 'peptide', or 'precursor'."
|
|
1288
2398
|
)
|
|
1289
2399
|
|
|
1290
2400
|
if rollup not in ["np", "panel"]:
|
|
1291
2401
|
raise ValueError(
|
|
1292
|
-
"
|
|
2402
|
+
"Unknown rollup = '{rollup}'. Supported rollup types are 'np' and 'panel'."
|
|
1293
2403
|
)
|
|
1294
2404
|
|
|
1295
2405
|
if analyte_type == "precursor" and rollup == "panel":
|
|
1296
2406
|
raise ValueError(
|
|
1297
|
-
"Precursor data is not available for panel rollup,
|
|
2407
|
+
"Precursor data is not available for panel rollup, use rollup = 'np' to get precursor data."
|
|
1298
2408
|
)
|
|
1299
2409
|
|
|
2410
|
+
# cast Sample Name to str to ensure correct interpretation of sample names like '001'
|
|
2411
|
+
dtype = {"Sample Name": str}
|
|
2412
|
+
|
|
1300
2413
|
if analyte_type == "protein":
|
|
1301
2414
|
if rollup == "np":
|
|
1302
|
-
return
|
|
2415
|
+
return download_df(
|
|
1303
2416
|
self._get_search_result_protein_data(analysis_id)[
|
|
1304
2417
|
"npLink"
|
|
1305
|
-
]["url"]
|
|
2418
|
+
]["url"],
|
|
2419
|
+
dtype=dtype,
|
|
1306
2420
|
)
|
|
1307
2421
|
elif rollup == "panel":
|
|
1308
|
-
return
|
|
2422
|
+
return download_df(
|
|
1309
2423
|
self._get_search_result_protein_data(analysis_id)[
|
|
1310
2424
|
"panelLink"
|
|
1311
|
-
]["url"]
|
|
2425
|
+
]["url"],
|
|
2426
|
+
dtype=dtype,
|
|
1312
2427
|
)
|
|
1313
2428
|
elif analyte_type == "peptide":
|
|
1314
2429
|
if rollup == "np":
|
|
1315
|
-
return
|
|
2430
|
+
return download_df(
|
|
1316
2431
|
self._get_search_result_peptide_data(analysis_id)[
|
|
1317
2432
|
"npLink"
|
|
1318
|
-
]["url"]
|
|
2433
|
+
]["url"],
|
|
2434
|
+
dtype=dtype,
|
|
1319
2435
|
)
|
|
1320
2436
|
elif rollup == "panel":
|
|
1321
|
-
return
|
|
2437
|
+
return download_df(
|
|
1322
2438
|
self._get_search_result_peptide_data(analysis_id)[
|
|
1323
2439
|
"panelLink"
|
|
1324
|
-
]["url"]
|
|
2440
|
+
]["url"],
|
|
2441
|
+
dtype=dtype,
|
|
1325
2442
|
)
|
|
1326
2443
|
else:
|
|
1327
|
-
return
|
|
2444
|
+
return download_df(
|
|
1328
2445
|
self.get_search_result_file_url(
|
|
1329
2446
|
analysis_id, filename="report.tsv"
|
|
1330
2447
|
)["url"]
|
|
@@ -1344,18 +2461,21 @@ class SeerSDK:
|
|
|
1344
2461
|
filename : str
|
|
1345
2462
|
Name of the file to be fetched. Files can be case insensitive and without file extensions.
|
|
1346
2463
|
|
|
1347
|
-
download_path : str
|
|
2464
|
+
download_path : str, optional
|
|
1348
2465
|
String flag denoting where the user wants the files downloaded. Can be local or absolute as long as the path is valid.
|
|
1349
2466
|
|
|
1350
2467
|
Returns
|
|
1351
2468
|
-------
|
|
1352
|
-
|
|
1353
|
-
|
|
2469
|
+
str
|
|
2470
|
+
Path to the downloaded file.
|
|
1354
2471
|
"""
|
|
1355
2472
|
|
|
1356
2473
|
if not download_path:
|
|
1357
2474
|
download_path = os.getcwd()
|
|
1358
2475
|
|
|
2476
|
+
if download_path.endswith("/"):
|
|
2477
|
+
download_path = download_path.rstrip("/")
|
|
2478
|
+
|
|
1359
2479
|
if not analysis_id:
|
|
1360
2480
|
raise ValueError("Analysis ID cannot be empty.")
|
|
1361
2481
|
|
|
@@ -1363,7 +2483,6 @@ class SeerSDK:
|
|
|
1363
2483
|
raise ValueError(
|
|
1364
2484
|
"Please specify a valid folder path as download path."
|
|
1365
2485
|
)
|
|
1366
|
-
|
|
1367
2486
|
file = self.get_search_result_file_url(analysis_id, filename)
|
|
1368
2487
|
file_url = file["url"]
|
|
1369
2488
|
filename = file["filename"]
|
|
@@ -1389,15 +2508,10 @@ class SeerSDK:
|
|
|
1389
2508
|
)
|
|
1390
2509
|
break
|
|
1391
2510
|
except:
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
filename = filename[-1]
|
|
1397
|
-
if not os.path.isdir(f"{name}/{filename}"):
|
|
1398
|
-
os.makedirs(f"{name}/")
|
|
1399
|
-
print(f"File {filename} downloaded successfully to {download_path}.")
|
|
1400
|
-
return
|
|
2511
|
+
dirname = f"{download_path}/{os.path.dirname(filename)}"
|
|
2512
|
+
if not os.path.isdir(f"{dirname}"):
|
|
2513
|
+
os.makedirs(f"{dirname}")
|
|
2514
|
+
return f"{download_path}/{filename}"
|
|
1401
2515
|
|
|
1402
2516
|
def get_search_result_file_url(self, analysis_id: str, filename: str):
|
|
1403
2517
|
"""
|
|
@@ -1416,12 +2530,19 @@ class SeerSDK:
|
|
|
1416
2530
|
file_url: dict[str, str]
|
|
1417
2531
|
Dictionary containing the 'url' and 'filename' of the file.
|
|
1418
2532
|
"""
|
|
2533
|
+
pas_dirname = os.path.dirname(filename)
|
|
1419
2534
|
if "." in filename:
|
|
1420
2535
|
filename = ".".join(filename.split(".")[:-1])
|
|
1421
2536
|
filename = filename.casefold()
|
|
1422
2537
|
|
|
2538
|
+
if pas_dirname:
|
|
2539
|
+
analysis_result_files = self.list_search_result_files(
|
|
2540
|
+
analysis_id, folder=pas_dirname
|
|
2541
|
+
)
|
|
2542
|
+
else:
|
|
2543
|
+
analysis_result_files = self.list_search_result_files(analysis_id)
|
|
2544
|
+
|
|
1423
2545
|
# Allow user to pass in filenames without an extension.
|
|
1424
|
-
analysis_result_files = self.list_search_result_files(analysis_id)
|
|
1425
2546
|
analysis_result_files_prefix_mapper = {
|
|
1426
2547
|
(".".join(x.split(".")[:-1])).casefold(): x
|
|
1427
2548
|
for x in analysis_result_files
|
|
@@ -1433,10 +2554,10 @@ class SeerSDK:
|
|
|
1433
2554
|
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."
|
|
1434
2555
|
)
|
|
1435
2556
|
|
|
1436
|
-
analysis_metadata = self.
|
|
2557
|
+
analysis_metadata = self.get_analysis(analysis_id)
|
|
1437
2558
|
if analysis_metadata.get("status") in ["Failed", None]:
|
|
1438
2559
|
raise ValueError("Cannot generate links for failed searches.")
|
|
1439
|
-
with self._get_auth_session() as s:
|
|
2560
|
+
with self._get_auth_session("getsearchresultfileurl") as s:
|
|
1440
2561
|
file_url = s.post(
|
|
1441
2562
|
f"{self._auth.url}api/v1/analysisResultFiles/getUrl",
|
|
1442
2563
|
json={
|
|
@@ -1452,7 +2573,7 @@ class SeerSDK:
|
|
|
1452
2573
|
response["filename"] = filename
|
|
1453
2574
|
return response
|
|
1454
2575
|
|
|
1455
|
-
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="
|
|
2576
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="2.0.0")
|
|
1456
2577
|
def get_analysis_result_files(
|
|
1457
2578
|
self,
|
|
1458
2579
|
analysis_id: str,
|
|
@@ -1578,7 +2699,7 @@ class SeerSDK:
|
|
|
1578
2699
|
continue
|
|
1579
2700
|
|
|
1580
2701
|
links = {
|
|
1581
|
-
k:
|
|
2702
|
+
k: download_df(v, is_tsv=k.endswith(".tsv"))
|
|
1582
2703
|
for k, v in links.items()
|
|
1583
2704
|
}
|
|
1584
2705
|
if download_path:
|
|
@@ -1595,7 +2716,7 @@ class SeerSDK:
|
|
|
1595
2716
|
|
|
1596
2717
|
return links
|
|
1597
2718
|
|
|
1598
|
-
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="
|
|
2719
|
+
@deprecation.deprecated(deprecated_in="0.3.0", removed_in="2.0.0")
|
|
1599
2720
|
def get_analysis_result(
|
|
1600
2721
|
self,
|
|
1601
2722
|
analysis_id: str,
|
|
@@ -1660,17 +2781,17 @@ class SeerSDK:
|
|
|
1660
2781
|
analysis_id, link=True
|
|
1661
2782
|
)
|
|
1662
2783
|
links = {
|
|
1663
|
-
"peptide_np":
|
|
1664
|
-
"peptide_panel":
|
|
1665
|
-
"protein_np":
|
|
1666
|
-
"protein_panel":
|
|
2784
|
+
"peptide_np": download_df(peptide_data["npLink"]["url"]),
|
|
2785
|
+
"peptide_panel": download_df(peptide_data["panelLink"]["url"]),
|
|
2786
|
+
"protein_np": download_df(protein_data["npLink"]["url"]),
|
|
2787
|
+
"protein_panel": download_df(protein_data["panelLink"]["url"]),
|
|
1667
2788
|
}
|
|
1668
2789
|
|
|
1669
2790
|
if diann_report:
|
|
1670
2791
|
diann_report_url = self._get_search_result_file_url(
|
|
1671
2792
|
analysis_id, "report.tsv"
|
|
1672
2793
|
)
|
|
1673
|
-
links["diann_report"] =
|
|
2794
|
+
links["diann_report"] = download_df(diann_report_url["url"])
|
|
1674
2795
|
|
|
1675
2796
|
if download_path:
|
|
1676
2797
|
name = f"{download_path}/downloads/{analysis_id}"
|
|
@@ -1723,7 +2844,7 @@ class SeerSDK:
|
|
|
1723
2844
|
raise ValueError("Analysis id cannot be empty.")
|
|
1724
2845
|
|
|
1725
2846
|
try:
|
|
1726
|
-
res = self.
|
|
2847
|
+
res = self.find_analyses(analysis_id)
|
|
1727
2848
|
except ValueError:
|
|
1728
2849
|
return ValueError("Analysis not found. Your ID could be incorrect")
|
|
1729
2850
|
|
|
@@ -1757,12 +2878,12 @@ class SeerSDK:
|
|
|
1757
2878
|
)
|
|
1758
2879
|
|
|
1759
2880
|
if not analysis_id and analysis_name:
|
|
1760
|
-
analysis_id = self.
|
|
2881
|
+
analysis_id = self.find_analyses(analysis_name=analysis_name)[0][
|
|
1761
2882
|
"id"
|
|
1762
2883
|
]
|
|
1763
2884
|
|
|
1764
2885
|
URL = self._auth.url + "api/v2/groupanalysis/protein"
|
|
1765
|
-
with self._get_auth_session() as s:
|
|
2886
|
+
with self._get_auth_session("getproteinresults") as s:
|
|
1766
2887
|
res = s.post(
|
|
1767
2888
|
URL, json={"analysisId": analysis_id, "grouping": grouping}
|
|
1768
2889
|
)
|
|
@@ -1826,12 +2947,12 @@ class SeerSDK:
|
|
|
1826
2947
|
)
|
|
1827
2948
|
|
|
1828
2949
|
if not analysis_id and analysis_name:
|
|
1829
|
-
analysis_id = self.
|
|
2950
|
+
analysis_id = self.find_analyses(analysis_name=analysis_name)[0][
|
|
1830
2951
|
"id"
|
|
1831
2952
|
]
|
|
1832
2953
|
|
|
1833
2954
|
URL = self._auth.url + "api/v2/groupanalysis/peptide"
|
|
1834
|
-
with self._get_auth_session() as s:
|
|
2955
|
+
with self._get_auth_session("getpeptideresults") as s:
|
|
1835
2956
|
res = s.post(
|
|
1836
2957
|
URL, json={"analysisId": analysis_id, "grouping": grouping}
|
|
1837
2958
|
)
|
|
@@ -1901,7 +3022,7 @@ class SeerSDK:
|
|
|
1901
3022
|
if not space
|
|
1902
3023
|
else f"{self._auth.url}api/v1/msdataindex/filesinfolder?folder={folder}&userGroupId={space}"
|
|
1903
3024
|
)
|
|
1904
|
-
with self._get_auth_session() as s:
|
|
3025
|
+
with self._get_auth_session("listmsdatafiles") as s:
|
|
1905
3026
|
|
|
1906
3027
|
files = s.get(URL)
|
|
1907
3028
|
|
|
@@ -1912,7 +3033,7 @@ class SeerSDK:
|
|
|
1912
3033
|
return files.json()["filesList"]
|
|
1913
3034
|
|
|
1914
3035
|
def download_ms_data_files(
|
|
1915
|
-
self, paths: _List[str], download_path: str, space: str = None
|
|
3036
|
+
self, paths: _List[str], download_path: str = "", space: str = None
|
|
1916
3037
|
):
|
|
1917
3038
|
"""
|
|
1918
3039
|
Downloads all MS data files for paths passed in the params to the specified download path.
|
|
@@ -1921,15 +3042,14 @@ class SeerSDK:
|
|
|
1921
3042
|
----------
|
|
1922
3043
|
paths : list[str]
|
|
1923
3044
|
List of paths to download.
|
|
1924
|
-
download_path : str
|
|
1925
|
-
Path to download the files to.
|
|
3045
|
+
download_path : str, optional
|
|
3046
|
+
Path to download the files to. Defaults to current working directory.
|
|
1926
3047
|
space : str, optional
|
|
1927
3048
|
ID of the user group to which the files belongs, defaulted to None.
|
|
1928
3049
|
|
|
1929
3050
|
Returns
|
|
1930
3051
|
-------
|
|
1931
|
-
|
|
1932
|
-
Contains the 'message' whether the files were downloaded or not.
|
|
3052
|
+
list[str] : the list of paths to the downloaded files.
|
|
1933
3053
|
"""
|
|
1934
3054
|
|
|
1935
3055
|
urls = []
|
|
@@ -1957,8 +3077,7 @@ class SeerSDK:
|
|
|
1957
3077
|
tenant_id = self._auth.active_tenant_id
|
|
1958
3078
|
|
|
1959
3079
|
for path in paths:
|
|
1960
|
-
with self._get_auth_session() as s:
|
|
1961
|
-
|
|
3080
|
+
with self._get_auth_session("getmsdataindexurl") as s:
|
|
1962
3081
|
download_url = s.post(
|
|
1963
3082
|
URL,
|
|
1964
3083
|
json={
|
|
@@ -1972,6 +3091,8 @@ class SeerSDK:
|
|
|
1972
3091
|
"Could not download file. Please check if the backend is running."
|
|
1973
3092
|
)
|
|
1974
3093
|
urls.append(download_url.text)
|
|
3094
|
+
|
|
3095
|
+
downloads = []
|
|
1975
3096
|
for i in range(len(urls)):
|
|
1976
3097
|
filename = paths[i].split("/")[-1]
|
|
1977
3098
|
url = urls[i]
|
|
@@ -1996,6 +3117,7 @@ class SeerSDK:
|
|
|
1996
3117
|
reporthook=download_hook(t),
|
|
1997
3118
|
data=None,
|
|
1998
3119
|
)
|
|
3120
|
+
downloads.append(f"{name}/{filename}")
|
|
1999
3121
|
break
|
|
2000
3122
|
except:
|
|
2001
3123
|
filename = filename.split("/")
|
|
@@ -2013,7 +3135,7 @@ class SeerSDK:
|
|
|
2013
3135
|
|
|
2014
3136
|
print(f"Finished downloading {filename}\n")
|
|
2015
3137
|
|
|
2016
|
-
return
|
|
3138
|
+
return downloads
|
|
2017
3139
|
|
|
2018
3140
|
def get_group_analysis(
|
|
2019
3141
|
self, analysis_id, group_analysis_id=None, **kwargs
|
|
@@ -2062,7 +3184,7 @@ class SeerSDK:
|
|
|
2062
3184
|
URL = f"{URL}/{group_analysis_id}"
|
|
2063
3185
|
params["id"] = group_analysis_id
|
|
2064
3186
|
|
|
2065
|
-
with self._get_auth_session() as s:
|
|
3187
|
+
with self._get_auth_session("getgroupanalyses") as s:
|
|
2066
3188
|
response = s.get(URL, params=params)
|
|
2067
3189
|
if response.status_code != 200:
|
|
2068
3190
|
raise ServerError(
|
|
@@ -2141,7 +3263,7 @@ class SeerSDK:
|
|
|
2141
3263
|
}
|
|
2142
3264
|
|
|
2143
3265
|
# Pre-GA data call
|
|
2144
|
-
with self._get_auth_session() as s:
|
|
3266
|
+
with self._get_auth_session("getanalysisresultprotein") as s:
|
|
2145
3267
|
|
|
2146
3268
|
protein_pre_data = s.post(
|
|
2147
3269
|
url=f"{URL}api/v2/groupanalysis/protein",
|
|
@@ -2156,7 +3278,7 @@ class SeerSDK:
|
|
|
2156
3278
|
|
|
2157
3279
|
res["pre"]["protein"] = protein_pre_data
|
|
2158
3280
|
|
|
2159
|
-
with self._get_auth_session() as s:
|
|
3281
|
+
with self._get_auth_session("getanalysisresultpeptide") as s:
|
|
2160
3282
|
|
|
2161
3283
|
peptide_pre_data = s.post(
|
|
2162
3284
|
url=f"{URL}api/v2/groupanalysis/peptide",
|
|
@@ -2172,7 +3294,7 @@ class SeerSDK:
|
|
|
2172
3294
|
res["pre"]["peptide"] = peptide_pre_data
|
|
2173
3295
|
|
|
2174
3296
|
# Post-GA data call
|
|
2175
|
-
with self._get_auth_session() as s:
|
|
3297
|
+
with self._get_auth_session("getgroupanalysispost") as s:
|
|
2176
3298
|
if group_analysis_id:
|
|
2177
3299
|
get_saved_result = self.get_group_analysis(
|
|
2178
3300
|
analysis_id=analysis_id,
|
|
@@ -2255,7 +3377,7 @@ class SeerSDK:
|
|
|
2255
3377
|
'proteinId', 'intensity', 'sampleName', 'sampleId', 'condition','gene'
|
|
2256
3378
|
"""
|
|
2257
3379
|
|
|
2258
|
-
with self._get_auth_session() as s:
|
|
3380
|
+
with self._get_auth_session("getgroupanalysisraw") as s:
|
|
2259
3381
|
|
|
2260
3382
|
# API call 1 - get volcano plot data for filtered results and gene mapping
|
|
2261
3383
|
builder = self.get_volcano_plot_data(
|
|
@@ -2411,7 +3533,7 @@ class SeerSDK:
|
|
|
2411
3533
|
|
|
2412
3534
|
URL = f"{self._auth.url}api/v1/analysisqcpca"
|
|
2413
3535
|
|
|
2414
|
-
with self._get_auth_session() as s:
|
|
3536
|
+
with self._get_auth_session("getanalysispca") as s:
|
|
2415
3537
|
json = {
|
|
2416
3538
|
"analysisIds": ",".join(analysis_ids),
|
|
2417
3539
|
"type": type,
|
|
@@ -2561,7 +3683,7 @@ class SeerSDK:
|
|
|
2561
3683
|
|
|
2562
3684
|
URL = f"{self._auth.url}api/v1/analysishcluster"
|
|
2563
3685
|
|
|
2564
|
-
with self._get_auth_session() as s:
|
|
3686
|
+
with self._get_auth_session("getanalysishclust") as s:
|
|
2565
3687
|
json = {
|
|
2566
3688
|
"analysisIds": ",".join(analysis_ids),
|
|
2567
3689
|
}
|
|
@@ -2617,7 +3739,7 @@ class SeerSDK:
|
|
|
2617
3739
|
|
|
2618
3740
|
URL = f"{self._auth.url}api/v1/groupanalysis/stringdb"
|
|
2619
3741
|
|
|
2620
|
-
with self._get_auth_session() as s:
|
|
3742
|
+
with self._get_auth_session("getppinetwork") as s:
|
|
2621
3743
|
json = {
|
|
2622
3744
|
"significantPGs": ",".join(significant_pgs),
|
|
2623
3745
|
}
|
|
@@ -2698,7 +3820,7 @@ class SeerSDK:
|
|
|
2698
3820
|
significantPGs=",".join(significant_pgs),
|
|
2699
3821
|
)
|
|
2700
3822
|
|
|
2701
|
-
with self._get_auth_session() as s:
|
|
3823
|
+
with self._get_auth_session("getanalysisclusterheatmap") as s:
|
|
2702
3824
|
URL = f"{self._auth.url}api/v2/clusterheatmap"
|
|
2703
3825
|
response = s.post(URL, json=payload)
|
|
2704
3826
|
if response.status_code != 200:
|
|
@@ -2737,7 +3859,7 @@ class SeerSDK:
|
|
|
2737
3859
|
if not significant_pgs:
|
|
2738
3860
|
raise ValueError("Significant pgs cannot be empty.")
|
|
2739
3861
|
|
|
2740
|
-
with self._get_auth_session() as s:
|
|
3862
|
+
with self._get_auth_session("getanalysisenrichmentplot") as s:
|
|
2741
3863
|
json = {
|
|
2742
3864
|
"analysisId": analysis_id,
|
|
2743
3865
|
"significantPGs": significant_pgs,
|
|
@@ -2827,12 +3949,12 @@ class SeerSDK:
|
|
|
2827
3949
|
if analysis_id:
|
|
2828
3950
|
rows = [{"id": analysis_id}]
|
|
2829
3951
|
else:
|
|
2830
|
-
rows = self.
|
|
3952
|
+
rows = self.find_analyses(analysis_name=analysis_name)
|
|
2831
3953
|
|
|
2832
3954
|
resp = []
|
|
2833
3955
|
for row in rows:
|
|
2834
3956
|
URL = f"{self._auth.url}api/v1/analyses/samples/{row['id']}"
|
|
2835
|
-
with self._get_auth_session() as s:
|
|
3957
|
+
with self._get_auth_session("getanalysissamples") as s:
|
|
2836
3958
|
samples = s.get(URL)
|
|
2837
3959
|
try:
|
|
2838
3960
|
samples.raise_for_status()
|
|
@@ -2841,15 +3963,15 @@ class SeerSDK:
|
|
|
2841
3963
|
except:
|
|
2842
3964
|
continue
|
|
2843
3965
|
|
|
2844
|
-
if not resp:
|
|
2845
|
-
raise ServerError(
|
|
2846
|
-
f"Could not retrieve samples for analysis {analysis_id or analysis_name}."
|
|
2847
|
-
)
|
|
2848
|
-
|
|
2849
3966
|
resp = pd.DataFrame(resp)
|
|
2850
3967
|
resp.drop_duplicates(subset=["id"], inplace=True)
|
|
2851
3968
|
return resp if as_df else resp.to_dict(orient="records")
|
|
2852
3969
|
|
|
3970
|
+
@deprecation.deprecated(
|
|
3971
|
+
deprecated_in="1.1.0",
|
|
3972
|
+
removed_in="2.0.0",
|
|
3973
|
+
details="get_analysis_protocol_fasta is deprecated. Use download_analysis_protocol_fasta instead.",
|
|
3974
|
+
)
|
|
2853
3975
|
def get_analysis_protocol_fasta(self, analysis_id, download_path=None):
|
|
2854
3976
|
if not analysis_id:
|
|
2855
3977
|
raise ValueError("Analysis ID cannot be empty.")
|
|
@@ -2858,14 +3980,14 @@ class SeerSDK:
|
|
|
2858
3980
|
download_path = os.getcwd()
|
|
2859
3981
|
|
|
2860
3982
|
try:
|
|
2861
|
-
analysis_protocol_id = self.
|
|
3983
|
+
analysis_protocol_id = self.find_analyses(analysis_id)[0][
|
|
2862
3984
|
"analysis_protocol_id"
|
|
2863
3985
|
]
|
|
2864
3986
|
except (IndexError, KeyError):
|
|
2865
3987
|
raise ValueError(f"Could not parse server response.")
|
|
2866
3988
|
|
|
2867
3989
|
try:
|
|
2868
|
-
analysis_protocol_engine = self.
|
|
3990
|
+
analysis_protocol_engine = self.find_analysis_protocols(
|
|
2869
3991
|
analysis_protocol_id=analysis_protocol_id
|
|
2870
3992
|
)[0]["analysis_engine"]
|
|
2871
3993
|
except (IndexError, KeyError):
|
|
@@ -2887,7 +4009,7 @@ class SeerSDK:
|
|
|
2887
4009
|
f"Analysis protocol engine {analysis_protocol_engine} not supported for fasta download."
|
|
2888
4010
|
)
|
|
2889
4011
|
|
|
2890
|
-
with self._get_auth_session() as s:
|
|
4012
|
+
with self._get_auth_session("getanalysisprotocolparameters") as s:
|
|
2891
4013
|
response = s.get(URL)
|
|
2892
4014
|
if response.status_code != 200:
|
|
2893
4015
|
raise ServerError("Request failed.")
|
|
@@ -2904,7 +4026,7 @@ class SeerSDK:
|
|
|
2904
4026
|
|
|
2905
4027
|
URL = f"{self._auth.url}api/v1/analysisProtocolFiles/getUrl"
|
|
2906
4028
|
for file in fasta_filenames:
|
|
2907
|
-
with self._get_auth_session() as s:
|
|
4029
|
+
with self._get_auth_session("getanalysisprotocolfilesurl") as s:
|
|
2908
4030
|
response = s.post(URL, json={"filepath": file})
|
|
2909
4031
|
if response.status_code != 200:
|
|
2910
4032
|
raise ServerError("Request failed.")
|
|
@@ -2935,3 +4057,172 @@ class SeerSDK:
|
|
|
2935
4057
|
os.makedirs(f"{download_path}")
|
|
2936
4058
|
|
|
2937
4059
|
print(f"Downloaded file to {download_path}/{file}")
|
|
4060
|
+
|
|
4061
|
+
def _get_analysis_protocol_fasta_filenames(
|
|
4062
|
+
self, analysis_protocol_id: str, analysis_protocol_engine: str
|
|
4063
|
+
):
|
|
4064
|
+
"""
|
|
4065
|
+
Helper function - Get the fasta file name(s) associated with a given analysis protocol and engine.
|
|
4066
|
+
Args:
|
|
4067
|
+
analysis_protocol_id (str): ID of the analysis protocol.
|
|
4068
|
+
analysis_protocol_engine (str): Analysis engine of the analysis protocol.
|
|
4069
|
+
Returns:
|
|
4070
|
+
list[str]: A list of fasta file names associated with the analysis protocol.
|
|
4071
|
+
"""
|
|
4072
|
+
analysis_protocol_engine = analysis_protocol_engine.lower()
|
|
4073
|
+
if analysis_protocol_engine == "diann":
|
|
4074
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/editableParameters/diann/{analysis_protocol_id}"
|
|
4075
|
+
elif analysis_protocol_engine == "encyclopedia":
|
|
4076
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/editableParameters/dia/{analysis_protocol_id}"
|
|
4077
|
+
elif analysis_protocol_engine == "msfragger":
|
|
4078
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/editableParameters/msfragger/{analysis_protocol_id}"
|
|
4079
|
+
elif analysis_protocol_engine == "proteogenomics":
|
|
4080
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/editableParameters/proteogenomics/{analysis_protocol_id}"
|
|
4081
|
+
elif analysis_protocol_engine == "maxquant":
|
|
4082
|
+
URL = f"{self._auth.url}api/v1/analysisProtocols/editableParameters/{analysis_protocol_id}"
|
|
4083
|
+
else:
|
|
4084
|
+
raise ValueError(
|
|
4085
|
+
f"Analysis protocol engine {analysis_protocol_engine} not supported for fasta download."
|
|
4086
|
+
)
|
|
4087
|
+
|
|
4088
|
+
with self._get_auth_session("getanalysisprotocolparameters") as s:
|
|
4089
|
+
response = s.get(URL)
|
|
4090
|
+
if response.status_code != 200:
|
|
4091
|
+
raise ServerError("Failed to retrieve analysis protocol data.")
|
|
4092
|
+
response = response.json()
|
|
4093
|
+
if isinstance(response, dict):
|
|
4094
|
+
response = response["editableParameters"]
|
|
4095
|
+
fasta_filenames = (
|
|
4096
|
+
np.array(
|
|
4097
|
+
[
|
|
4098
|
+
x["Value"]
|
|
4099
|
+
for x in response
|
|
4100
|
+
if x["Key"]
|
|
4101
|
+
in ["fasta", "fastaFilePath", "referencegenome"]
|
|
4102
|
+
]
|
|
4103
|
+
)
|
|
4104
|
+
.flatten()
|
|
4105
|
+
.tolist()
|
|
4106
|
+
)
|
|
4107
|
+
if not fasta_filenames:
|
|
4108
|
+
raise ServerError("No fasta file name returned from server.")
|
|
4109
|
+
return fasta_filenames
|
|
4110
|
+
|
|
4111
|
+
def get_analysis_protocol_fasta_link(
|
|
4112
|
+
self, analysis_protocol_id=None, analysis_id=None, analysis_name=None
|
|
4113
|
+
):
|
|
4114
|
+
"""Get the download link(s) for the fasta file(s) associated with a given analysis protocol.
|
|
4115
|
+
Args:
|
|
4116
|
+
analysis_protocol_id (str,optional): ID of the analysis protocol. Defaults to None.
|
|
4117
|
+
analysis_id (str, optional): ID of the analysis. Defaults to None.
|
|
4118
|
+
analysis_name (str, optional): Name of the analysis. Defaults to None.
|
|
4119
|
+
|
|
4120
|
+
Returns:
|
|
4121
|
+
list[dict]: A list of dictionaries containing the 'filename' and the 'url' to download the fasta file.
|
|
4122
|
+
"""
|
|
4123
|
+
if analysis_name and (not analysis_id):
|
|
4124
|
+
analyses = self.find_analyses(analysis_name=analysis_name)
|
|
4125
|
+
if len(analyses) > 1:
|
|
4126
|
+
raise ValueError(
|
|
4127
|
+
f"Multiple analyses found with name {analysis_name}. Please provide an analysis ID instead."
|
|
4128
|
+
)
|
|
4129
|
+
elif len(analyses) == 0:
|
|
4130
|
+
raise ValueError(
|
|
4131
|
+
f"No analyses found with name {analysis_name}."
|
|
4132
|
+
)
|
|
4133
|
+
else:
|
|
4134
|
+
analysis_id = analyses[0]["id"]
|
|
4135
|
+
|
|
4136
|
+
if not (bool(analysis_protocol_id) ^ bool(analysis_id)):
|
|
4137
|
+
raise ValueError(
|
|
4138
|
+
"Please provide either an analysis ID or an analysis protocol ID."
|
|
4139
|
+
)
|
|
4140
|
+
|
|
4141
|
+
if not analysis_protocol_id:
|
|
4142
|
+
try:
|
|
4143
|
+
analysis_protocol_id = self.get_analysis(
|
|
4144
|
+
analysis_id=analysis_id
|
|
4145
|
+
)["analysis_protocol_id"]
|
|
4146
|
+
except KeyError:
|
|
4147
|
+
raise ValueError(f"Could not parse server response.")
|
|
4148
|
+
|
|
4149
|
+
engine = self.get_analysis_protocol(
|
|
4150
|
+
analysis_protocol_id=analysis_protocol_id
|
|
4151
|
+
).get("analysis_engine", None)
|
|
4152
|
+
fasta_filenames = self._get_analysis_protocol_fasta_filenames(
|
|
4153
|
+
analysis_protocol_id=analysis_protocol_id,
|
|
4154
|
+
analysis_protocol_engine=engine,
|
|
4155
|
+
)
|
|
4156
|
+
URL = f"{self._auth.url}api/v1/analysisProtocolFiles/getUrl"
|
|
4157
|
+
links = []
|
|
4158
|
+
for file in fasta_filenames:
|
|
4159
|
+
with self._get_auth_session("getanalysisprotocolfilesurl") as s:
|
|
4160
|
+
filename = os.path.basename(file)
|
|
4161
|
+
response = s.post(URL, json={"filepath": file})
|
|
4162
|
+
if response.status_code != 200:
|
|
4163
|
+
print(
|
|
4164
|
+
f"ERROR: Could not retrieve download link for {filename}."
|
|
4165
|
+
)
|
|
4166
|
+
continue
|
|
4167
|
+
url = response.json()["url"]
|
|
4168
|
+
links.append({"filename": filename, "url": url})
|
|
4169
|
+
return links
|
|
4170
|
+
|
|
4171
|
+
def download_analysis_protocol_fasta(
|
|
4172
|
+
self,
|
|
4173
|
+
analysis_protocol_id=None,
|
|
4174
|
+
analysis_id=None,
|
|
4175
|
+
analysis_name=None,
|
|
4176
|
+
download_path=None,
|
|
4177
|
+
):
|
|
4178
|
+
"""Download the fasta file(s) associated with a given analysis protocol.
|
|
4179
|
+
|
|
4180
|
+
Args:
|
|
4181
|
+
analysis_protocol_id (str,optional): ID of the analysis protocol. Defaults to None.
|
|
4182
|
+
analysis_id (str, optional): ID of the analysis. Defaults to None.
|
|
4183
|
+
analysis_name (str, optional): Name of the analysis. Defaults to None.
|
|
4184
|
+
download_path (str, optional): Path to download the fasta file to. Defaults to current working directory.
|
|
4185
|
+
|
|
4186
|
+
Returns:
|
|
4187
|
+
list[str] : The path to the downloaded fasta file(s).
|
|
4188
|
+
"""
|
|
4189
|
+
|
|
4190
|
+
links = [
|
|
4191
|
+
(x["filename"], x["url"])
|
|
4192
|
+
for x in self.get_analysis_protocol_fasta_link(
|
|
4193
|
+
analysis_protocol_id=analysis_protocol_id,
|
|
4194
|
+
analysis_id=analysis_id,
|
|
4195
|
+
analysis_name=analysis_name,
|
|
4196
|
+
)
|
|
4197
|
+
]
|
|
4198
|
+
if not download_path:
|
|
4199
|
+
download_path = os.getcwd()
|
|
4200
|
+
|
|
4201
|
+
downloads = []
|
|
4202
|
+
for filename, url in links:
|
|
4203
|
+
print(f"Downloading {filename}")
|
|
4204
|
+
for _ in range(2):
|
|
4205
|
+
try:
|
|
4206
|
+
with tqdm(
|
|
4207
|
+
unit="B",
|
|
4208
|
+
unit_scale=True,
|
|
4209
|
+
unit_divisor=1024,
|
|
4210
|
+
miniters=1,
|
|
4211
|
+
desc=f"Progress",
|
|
4212
|
+
) as t:
|
|
4213
|
+
ssl._create_default_https_context = (
|
|
4214
|
+
ssl._create_unverified_context
|
|
4215
|
+
)
|
|
4216
|
+
urllib.request.urlretrieve(
|
|
4217
|
+
url,
|
|
4218
|
+
f"{download_path}/{filename}",
|
|
4219
|
+
reporthook=download_hook(t),
|
|
4220
|
+
data=None,
|
|
4221
|
+
)
|
|
4222
|
+
break
|
|
4223
|
+
except:
|
|
4224
|
+
if not os.path.isdir(f"{download_path}"):
|
|
4225
|
+
os.makedirs(f"{download_path}")
|
|
4226
|
+
|
|
4227
|
+
downloads.append(f"{download_path}/{filename}")
|
|
4228
|
+
return downloads
|