seqslab-cli 3.3.6__tar.gz → 3.3.8__tar.gz
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.
- {seqslab-cli-3.3.6/python/seqslab_cli.egg-info → seqslab-cli-3.3.8}/PKG-INFO +1 -1
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/commands.py +54 -15
- seqslab-cli-3.3.8/python/seqslab/auth/utils.py +46 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/commands.py +2 -49
- seqslab-cli-3.3.8/python/seqslab/organization/commands.py +117 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/runsheet/runsheet.py +11 -2
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/commands.py +2 -3
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/commands.py +41 -32
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/internal/parameters.py +74 -59
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/base.py +15 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/base.py +5 -1
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/SOURCES.txt +1 -0
- seqslab-cli-3.3.6/python/seqslab/organization/commands.py +0 -77
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/LICENSE +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/MANIFEST.in +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/README.md +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/azuread.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/cli.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/color.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/aiocopy.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/utils.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/biomimetype.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/commands.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/internal/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/settings.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/commands.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/commands.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/internal/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/__init__.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/azure.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/base.py +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/requires.txt +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/requirements.txt +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/setup.cfg +0 -0
- {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/setup.py +0 -0
|
@@ -19,6 +19,7 @@ from requests import HTTPError, request
|
|
|
19
19
|
from seqslab import settings as api_settings
|
|
20
20
|
from seqslab.auth import __version__, aad_app, azuread
|
|
21
21
|
from seqslab.auth.azuread import API_HOSTNAME
|
|
22
|
+
from seqslab.auth.utils import get_org, request_wrap
|
|
22
23
|
from termcolor import cprint
|
|
23
24
|
|
|
24
25
|
"""
|
|
@@ -44,12 +45,10 @@ class BaseAuth:
|
|
|
44
45
|
AUTHORIZATION_URL = f"https://{API_HOSTNAME}/auth/{__version__}/signin/{{backend}}/"
|
|
45
46
|
ACCESS_TOKEN_URL = f"https://{API_HOSTNAME}/auth/{__version__}/token/{{backend}}/"
|
|
46
47
|
REFRESH_TOKEN_URL = f"https://{API_HOSTNAME}/auth/{__version__}/token/refresh/"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
response.raise_for_status()
|
|
52
|
-
return response
|
|
48
|
+
PROFILE_URL = f"https://{API_HOSTNAME}/auth/{__version__}/profile/"
|
|
49
|
+
ORGANIZATION_GET_URL = (
|
|
50
|
+
f"https://{API_HOSTNAME}/management/{__version__}/organizations/{{cus_id}}/"
|
|
51
|
+
)
|
|
53
52
|
|
|
54
53
|
def _signin_azure_silent(
|
|
55
54
|
self,
|
|
@@ -76,7 +75,7 @@ class BaseAuth:
|
|
|
76
75
|
)
|
|
77
76
|
|
|
78
77
|
result.update({"assertion": assertion, "scope": " ".join(scopes)})
|
|
79
|
-
return
|
|
78
|
+
return request_wrap(
|
|
80
79
|
self.ACCESS_TOKEN_URL.format(backend=backend),
|
|
81
80
|
method=self.ACCESS_TOKEN_METHOD,
|
|
82
81
|
params={"secure": True},
|
|
@@ -89,7 +88,12 @@ class BaseAuth:
|
|
|
89
88
|
).json()
|
|
90
89
|
|
|
91
90
|
def _signin_azure(
|
|
92
|
-
self,
|
|
91
|
+
self,
|
|
92
|
+
device_code: bool,
|
|
93
|
+
daemon: bool,
|
|
94
|
+
backend: str,
|
|
95
|
+
organization_id: str,
|
|
96
|
+
proxy: str = None,
|
|
93
97
|
) -> dict:
|
|
94
98
|
result = None
|
|
95
99
|
token_uri = self.ACCESS_TOKEN_URL.format(backend=backend)
|
|
@@ -121,7 +125,7 @@ class BaseAuth:
|
|
|
121
125
|
if not device_code:
|
|
122
126
|
# obtain authorization uri
|
|
123
127
|
auth_uri = (
|
|
124
|
-
|
|
128
|
+
request_wrap(
|
|
125
129
|
self.AUTHORIZATION_URL.format(backend=backend),
|
|
126
130
|
method=self.ACCESS_TOKEN_METHOD,
|
|
127
131
|
headers={
|
|
@@ -176,8 +180,10 @@ class BaseAuth:
|
|
|
176
180
|
raise PermissionError(
|
|
177
181
|
"{}: {}".format(result["error"], result["error_description"])
|
|
178
182
|
)
|
|
183
|
+
if organization_id:
|
|
184
|
+
result.update({"cus_id": organization_id})
|
|
179
185
|
|
|
180
|
-
return
|
|
186
|
+
return request_wrap(
|
|
181
187
|
token_uri,
|
|
182
188
|
method=self.ACCESS_TOKEN_METHOD,
|
|
183
189
|
params={"secure": True} if daemon else None,
|
|
@@ -230,7 +236,7 @@ class BaseAuth:
|
|
|
230
236
|
if arrow.utcnow() >= arrow.get(Auth.tokens["attrs"]["exp"]):
|
|
231
237
|
# expired, refresh token
|
|
232
238
|
proxy = context.get_context().args.proxy
|
|
233
|
-
with
|
|
239
|
+
with request_wrap(
|
|
234
240
|
Auth.REFRESH_TOKEN_URL,
|
|
235
241
|
method=Auth.ACCESS_TOKEN_METHOD,
|
|
236
242
|
json={"refresh": Auth.tokens["tokens"]["refresh"]},
|
|
@@ -270,13 +276,27 @@ class BaseAuth:
|
|
|
270
276
|
"daemon",
|
|
271
277
|
type=bool,
|
|
272
278
|
positional=False,
|
|
273
|
-
description="Sign in for a long-running, non-interactive daemon process (optional).",
|
|
279
|
+
description="Sign in for a long-running, non-interactive daemon process. (optional).",
|
|
274
280
|
aliases=["d"],
|
|
275
281
|
)
|
|
276
|
-
|
|
282
|
+
@argument(
|
|
283
|
+
"organization_id",
|
|
284
|
+
type=str,
|
|
285
|
+
positional=False,
|
|
286
|
+
description="The organization id to signin to (optional). This argument is valid only for device_code flow.",
|
|
287
|
+
aliases=["o"],
|
|
288
|
+
)
|
|
289
|
+
def signin(
|
|
290
|
+
self, device_code: bool = False, daemon: bool = False, organization_id=None
|
|
291
|
+
) -> int:
|
|
277
292
|
"""
|
|
278
293
|
Sign in to the configured authentication backend and obtain API access token.
|
|
279
294
|
"""
|
|
295
|
+
if organization_id and not device_code:
|
|
296
|
+
logging.warning("Organization_id is only valid for device code flow")
|
|
297
|
+
cprint("Organization_id is valid only for device cod flow.", "red")
|
|
298
|
+
return errno.EINVAL
|
|
299
|
+
|
|
280
300
|
ctx = context.get_context()
|
|
281
301
|
backend = ctx.args.backend
|
|
282
302
|
proxy = ctx.args.proxy
|
|
@@ -289,9 +309,10 @@ class BaseAuth:
|
|
|
289
309
|
|
|
290
310
|
try:
|
|
291
311
|
method = getattr(self, mname)
|
|
292
|
-
result = method(device_code, daemon, backend, proxy)
|
|
312
|
+
result = method(device_code, daemon, backend, organization_id, proxy)
|
|
293
313
|
# store in keyring secret service
|
|
294
314
|
user = getpass.getuser()
|
|
315
|
+
|
|
295
316
|
keyring.set_password(
|
|
296
317
|
"net.seqslab.api.tokens.refresh", user, result["tokens"]["refresh"]
|
|
297
318
|
)
|
|
@@ -313,7 +334,8 @@ class BaseAuth:
|
|
|
313
334
|
@command(
|
|
314
335
|
aliases=["daemon"],
|
|
315
336
|
help="Authenticate silently to the platform and obtain API access token. "
|
|
316
|
-
"This requires that the SeqsLab API app admin consent has been granted
|
|
337
|
+
"This requires that the SeqsLab API app admin consent has been granted and also the user must "
|
|
338
|
+
"daemon-signin beforehand.",
|
|
317
339
|
)
|
|
318
340
|
@argument(
|
|
319
341
|
"scope",
|
|
@@ -362,6 +384,15 @@ class BaseAuth:
|
|
|
362
384
|
keyring.set_password(
|
|
363
385
|
"net.seqslab.api.tokens.access", user, result["tokens"]["access"]
|
|
364
386
|
)
|
|
387
|
+
org_info = get_org(
|
|
388
|
+
access_token=result["tokens"]["access"],
|
|
389
|
+
cus_id=self._decode(result["tokens"]["access"])["cus_id"],
|
|
390
|
+
proxy=proxy,
|
|
391
|
+
)
|
|
392
|
+
cprint(
|
|
393
|
+
f"Daemon process signin to {org_info['name']}",
|
|
394
|
+
"yellow",
|
|
395
|
+
)
|
|
365
396
|
return 0
|
|
366
397
|
except ValueError:
|
|
367
398
|
return errno.EINVAL
|
|
@@ -381,11 +412,19 @@ class BaseAuth:
|
|
|
381
412
|
"""
|
|
382
413
|
Print platform access token.
|
|
383
414
|
"""
|
|
415
|
+
ctx = context.get_context()
|
|
416
|
+
proxy = ctx.args.proxy
|
|
384
417
|
token = self.get_token()
|
|
385
418
|
if not token:
|
|
386
419
|
cprint("Not signed in yet")
|
|
387
420
|
return errno.EPERM
|
|
388
421
|
|
|
422
|
+
org_info = get_org(
|
|
423
|
+
access_token=token["tokens"]["access"],
|
|
424
|
+
cus_id=self._decode(token["tokens"]["access"])["cus_id"],
|
|
425
|
+
proxy=proxy,
|
|
426
|
+
)
|
|
427
|
+
cprint(f"For Organization: {org_info['name']} ({org_info['cus_id']})", "yellow")
|
|
389
428
|
cprint(token["tokens"]["access"], "yellow")
|
|
390
429
|
return 0
|
|
391
430
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Standard Library
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
from requests import request
|
|
5
|
+
from seqslab.auth import __version__, aad_app, azuread
|
|
6
|
+
from seqslab.auth.azuread import API_HOSTNAME
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Copyright (C) 2024, Atgenomix Incorporated.
|
|
10
|
+
|
|
11
|
+
All Rights Reserved.
|
|
12
|
+
|
|
13
|
+
This program is an unpublished copyrighted work which is proprietary to
|
|
14
|
+
Atgenomix Incorporated and contains confidential information that is not to
|
|
15
|
+
be reproduced or disclosed to any other person or entity without prior
|
|
16
|
+
written consent from Atgenomix, Inc. in each and every instance.
|
|
17
|
+
|
|
18
|
+
Unauthorized reproduction of this program as well as unauthorized
|
|
19
|
+
preparation of derivative works based upon the program or distribution of
|
|
20
|
+
copies by sale, rental, lease or lending are violations of federal copyright
|
|
21
|
+
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
ORGANIZATION_GET_URL = (
|
|
25
|
+
f"https://{API_HOSTNAME}/management/{__version__}/organizations/{{cus_id}}/"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def request_wrap(url, method, *args, **kwargs):
|
|
30
|
+
response = request(method, url, **kwargs)
|
|
31
|
+
response.raise_for_status()
|
|
32
|
+
return response
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@lru_cache(maxsize=16)
|
|
36
|
+
def get_org(access_token, cus_id, proxy):
|
|
37
|
+
return request_wrap(
|
|
38
|
+
ORGANIZATION_GET_URL.format(cus_id=cus_id),
|
|
39
|
+
method="GET",
|
|
40
|
+
headers={
|
|
41
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
42
|
+
"Accept": "application/json",
|
|
43
|
+
"Authorization": f"Bearer {access_token}",
|
|
44
|
+
},
|
|
45
|
+
proxies=proxy,
|
|
46
|
+
).json()
|
|
@@ -886,52 +886,6 @@ class BaseDatahub:
|
|
|
886
886
|
|
|
887
887
|
return results
|
|
888
888
|
|
|
889
|
-
@command(aliases=["deregister"])
|
|
890
|
-
@argument(
|
|
891
|
-
"id",
|
|
892
|
-
type=List[str],
|
|
893
|
-
positional=False,
|
|
894
|
-
description="Specify the IDs of the DRS objects that you want to delete "
|
|
895
|
-
"(optional).",
|
|
896
|
-
)
|
|
897
|
-
@argument(
|
|
898
|
-
"names",
|
|
899
|
-
type=List[str],
|
|
900
|
-
positional=False,
|
|
901
|
-
description="Specify the names of the DRS objects that you want to delete "
|
|
902
|
-
"(optional).",
|
|
903
|
-
)
|
|
904
|
-
@argument(
|
|
905
|
-
"tags",
|
|
906
|
-
type=List[str],
|
|
907
|
-
positional=False,
|
|
908
|
-
description="Specify the labels of the DRS objects that you want to delete "
|
|
909
|
-
"(optional).",
|
|
910
|
-
)
|
|
911
|
-
def deregister(
|
|
912
|
-
self,
|
|
913
|
-
id: List[str] = [],
|
|
914
|
-
tags: List[str] = [],
|
|
915
|
-
names: List[str] = [],
|
|
916
|
-
) -> int:
|
|
917
|
-
"""
|
|
918
|
-
Deregister DRS objects by DRS ID, DRS name, or DRS tag.
|
|
919
|
-
"""
|
|
920
|
-
if not id and not names and not tags:
|
|
921
|
-
cprint(
|
|
922
|
-
"Must specify one of the IDs, names or tags to identify DRS objects for deletion",
|
|
923
|
-
"red",
|
|
924
|
-
)
|
|
925
|
-
return errno.ENOENT
|
|
926
|
-
|
|
927
|
-
resps = asyncio.run(
|
|
928
|
-
utils.drs_delete(id, names, tags, query_opts="?backend_content=false")
|
|
929
|
-
)
|
|
930
|
-
for r in resps:
|
|
931
|
-
cprint(r, "yellow")
|
|
932
|
-
|
|
933
|
-
return 0
|
|
934
|
-
|
|
935
889
|
@command(aliases=["clean"])
|
|
936
890
|
@argument(
|
|
937
891
|
"id",
|
|
@@ -1535,9 +1489,8 @@ class BaseDatahub:
|
|
|
1535
1489
|
add_reads = False
|
|
1536
1490
|
for sa in rs.SampleSheet.samples:
|
|
1537
1491
|
if rs.SampleSheet.is_single_end:
|
|
1538
|
-
assert (
|
|
1539
|
-
|
|
1540
|
-
and sa.to_json().get("Add_Read2_Label") == ""
|
|
1492
|
+
assert not sa.to_json().get("Add_Read2_ID") and not sa.to_json().get(
|
|
1493
|
+
"Add_Read2_Label"
|
|
1541
1494
|
), "columns Add_Read2_ID and Add_Read2_Label should be blank for single_end sequencer run"
|
|
1542
1495
|
if (add_read1_id := sa.to_json().get("Add_Read1_ID")) and (
|
|
1543
1496
|
add_read1_label := sa.to_json().get("Add_Read1_Label")
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Standard Library
|
|
3
|
+
import errno
|
|
4
|
+
import getpass
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import keyring
|
|
8
|
+
import requests
|
|
9
|
+
import yarl
|
|
10
|
+
from nubia import argument, command, context
|
|
11
|
+
from requests import HTTPError
|
|
12
|
+
from seqslab.auth.commands import BaseAuth as Auth
|
|
13
|
+
from seqslab.auth.utils import get_org
|
|
14
|
+
from seqslab.organization.resource.base import BaseResource
|
|
15
|
+
from termcolor import cprint
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Copyright (C) 2023, Atgenomix Incorporated.
|
|
19
|
+
|
|
20
|
+
All Rights Reserved.
|
|
21
|
+
|
|
22
|
+
This program is an unpublished copyrighted work which is proprietary to
|
|
23
|
+
Atgenomix Incorporated and contains confidential information that is not to
|
|
24
|
+
be reproduced or disclosed to any other person or entity without prior
|
|
25
|
+
written consent from Atgenomix, Inc. in each and every instance.
|
|
26
|
+
|
|
27
|
+
Unauthorized reproduction of this program as well as unauthorized
|
|
28
|
+
preparation of derivative works based upon the program or distribution of
|
|
29
|
+
copies by sale, rental, lease or lending are violations of federal copyright
|
|
30
|
+
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BaseOrg:
|
|
35
|
+
@command
|
|
36
|
+
def list(self, **kwargs) -> int:
|
|
37
|
+
"""
|
|
38
|
+
List organizations to which the current user belongs
|
|
39
|
+
"""
|
|
40
|
+
ctx = context.get_context()
|
|
41
|
+
backend = ctx.args.backend
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
token = Auth.get_token().get("tokens").get("access")
|
|
45
|
+
organizations = (
|
|
46
|
+
BaseResource.request_wrapper(
|
|
47
|
+
callback=requests.get,
|
|
48
|
+
url=Auth.PROFILE_URL.format(backend=backend),
|
|
49
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
50
|
+
status=[requests.codes.ok],
|
|
51
|
+
)
|
|
52
|
+
.json()
|
|
53
|
+
.get("organizations")
|
|
54
|
+
)
|
|
55
|
+
for index, org_id in enumerate(organizations):
|
|
56
|
+
values = organizations[org_id]
|
|
57
|
+
cprint(
|
|
58
|
+
f"{values['name']} ({org_id})",
|
|
59
|
+
"yellow",
|
|
60
|
+
)
|
|
61
|
+
except HTTPError as e:
|
|
62
|
+
cprint(str(e), "red")
|
|
63
|
+
return errno.EPROTO
|
|
64
|
+
else:
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
@command
|
|
68
|
+
@argument(
|
|
69
|
+
"id",
|
|
70
|
+
type=str,
|
|
71
|
+
positional=False,
|
|
72
|
+
description="Specify an organization ID (required).",
|
|
73
|
+
)
|
|
74
|
+
def switch(self, id, **kwargs) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Switch organizations
|
|
77
|
+
"""
|
|
78
|
+
ctx = context.get_context()
|
|
79
|
+
backend = ctx.args.backend
|
|
80
|
+
proxy = ctx.args.proxy
|
|
81
|
+
user = getpass.getuser()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
token = Auth.get_token().get("tokens").get("access")
|
|
85
|
+
resp = BaseResource.request_wrapper(
|
|
86
|
+
callback=requests.get,
|
|
87
|
+
url=f"{Auth.AUTHORIZATION_URL.format(backend=backend)}{id}/",
|
|
88
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
89
|
+
status=[requests.codes.ok],
|
|
90
|
+
).json()
|
|
91
|
+
|
|
92
|
+
keyring.set_password(
|
|
93
|
+
"net.seqslab.api.tokens.refresh", user, resp["tokens"]["refresh"]
|
|
94
|
+
)
|
|
95
|
+
keyring.set_password(
|
|
96
|
+
"net.seqslab.api.tokens.access", user, resp["tokens"]["access"]
|
|
97
|
+
)
|
|
98
|
+
org_info = get_org(
|
|
99
|
+
access_token=token, cus_id=Auth._decode(token)["cus_id"], proxy=proxy
|
|
100
|
+
)
|
|
101
|
+
cprint(
|
|
102
|
+
f"The organization you are currently signed into: {org_info['name']}.",
|
|
103
|
+
"yellow",
|
|
104
|
+
)
|
|
105
|
+
except HTTPError as e:
|
|
106
|
+
cprint(str(e), "red")
|
|
107
|
+
return errno.EPROTO
|
|
108
|
+
else:
|
|
109
|
+
return 0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@command
|
|
113
|
+
class Org(BaseOrg):
|
|
114
|
+
"""Organization commands"""
|
|
115
|
+
|
|
116
|
+
def __init__(self):
|
|
117
|
+
super().__init__()
|
|
@@ -16,18 +16,25 @@ class Run:
|
|
|
16
16
|
- ``"Run_Name"``
|
|
17
17
|
- ``"Workflow_URL"``
|
|
18
18
|
- ``"Runtimes"``
|
|
19
|
+
- ``"Run_Schedule_ID``
|
|
19
20
|
|
|
20
21
|
A run may include multiple samples. For samples in a Run Sheet, samples with the same Run_Name will be clustered
|
|
21
22
|
as a single run.
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
def __init__(
|
|
25
|
-
self,
|
|
26
|
+
self,
|
|
27
|
+
samples: List[Sample],
|
|
28
|
+
run_name: str,
|
|
29
|
+
workflow_url: str,
|
|
30
|
+
runtimes: str,
|
|
31
|
+
run_schedule_id: str = None,
|
|
26
32
|
) -> None:
|
|
27
33
|
self.sample_sheet: Optional[SampleSheet] = None
|
|
28
34
|
self.run_name = run_name
|
|
29
35
|
self.workflow_url = workflow_url
|
|
30
36
|
self.runtimes = runtimes
|
|
37
|
+
self.run_schedule_id = run_schedule_id
|
|
31
38
|
self.samples = []
|
|
32
39
|
if not workflow_url.endswith("/"):
|
|
33
40
|
raise ValueError(
|
|
@@ -40,6 +47,7 @@ class Run:
|
|
|
40
47
|
s.get("Run_Name") == self.run_name
|
|
41
48
|
and s.get("Workflow_URL") == self.workflow_url
|
|
42
49
|
and s.get("Runtimes") == self.runtimes
|
|
50
|
+
and s.get("Run_Schedule_ID") == self.run_schedule_id
|
|
43
51
|
):
|
|
44
52
|
self.samples.append(s)
|
|
45
53
|
|
|
@@ -162,6 +170,7 @@ class RunSheet:
|
|
|
162
170
|
sample.get("Run_Name"),
|
|
163
171
|
sample.get("Workflow_URL"),
|
|
164
172
|
sample.get("Runtimes"),
|
|
173
|
+
sample.get("Run_Schedule_ID"),
|
|
165
174
|
)
|
|
166
175
|
rn = sample.get("Run_Name")
|
|
167
176
|
if rsig in runs and rn in run_name_set:
|
|
@@ -174,7 +183,7 @@ class RunSheet:
|
|
|
174
183
|
f"Inconsistent run_name/run_name_set {rn}/{run_name_set} and run sig/runs {rsig}/{runs.keys()}"
|
|
175
184
|
)
|
|
176
185
|
for k, v in runs.items():
|
|
177
|
-
self._runs.append(Run(v, k[0], k[1], k[2]))
|
|
186
|
+
self._runs.append(Run(v, k[0], k[1], k[2], k[3]))
|
|
178
187
|
|
|
179
188
|
@property
|
|
180
189
|
def runs(self) -> List:
|
|
@@ -149,12 +149,11 @@ class BaseUser:
|
|
|
149
149
|
if email.find("@") == -1:
|
|
150
150
|
cprint("Please give a legitimate email", "red")
|
|
151
151
|
return errno.ENOENT
|
|
152
|
-
|
|
153
152
|
ret = backend.add_user(
|
|
154
153
|
email=email,
|
|
155
154
|
roles=roles,
|
|
156
155
|
active=not deactivate,
|
|
157
|
-
name=name if
|
|
156
|
+
name=name if name else email.split("@")[0],
|
|
158
157
|
)
|
|
159
158
|
if isinstance(ret, int):
|
|
160
159
|
return ret
|
|
@@ -214,7 +213,7 @@ class BaseUser:
|
|
|
214
213
|
if roles:
|
|
215
214
|
payload["roles"] = roles
|
|
216
215
|
if name:
|
|
217
|
-
payload["
|
|
216
|
+
payload["username"] = name
|
|
218
217
|
if activate and not deactivate:
|
|
219
218
|
payload["is_active"] = True
|
|
220
219
|
elif deactivate and not activate:
|
|
@@ -19,13 +19,13 @@ from seqslab.auth.commands import BaseAuth
|
|
|
19
19
|
from seqslab.exceptions import exception_handler
|
|
20
20
|
from seqslab.runsheet.runsheet import Run, RunSheet
|
|
21
21
|
from seqslab.trs.register.common import trs_register
|
|
22
|
-
from seqslab.wes import API_HOSTNAME, __version__
|
|
23
|
-
from seqslab.wes.internal import parameters
|
|
24
22
|
from seqslab.workspace.internal.common import get_factory as get_workspace_factory
|
|
25
23
|
from tenacity import retry, stop_after_attempt, wait_fixed
|
|
26
24
|
from termcolor import cprint
|
|
27
25
|
from tzlocal import get_localzone
|
|
28
26
|
|
|
27
|
+
from . import API_HOSTNAME, __version__
|
|
28
|
+
from .internal import parameters
|
|
29
29
|
from .resource.common import get_factory
|
|
30
30
|
|
|
31
31
|
"""
|
|
@@ -494,38 +494,32 @@ class BaseJobs:
|
|
|
494
494
|
request_path = f"{working_dir}/{rpath}-request.json"
|
|
495
495
|
wf_info = run.workflow_url.split("versions")[1].strip("/").split("/")
|
|
496
496
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
)
|
|
497
|
+
resource = get_factory().load_resource()
|
|
498
|
+
if run.run_schedule_id:
|
|
499
|
+
req = resource.get_schedule(run.run_schedule_id).get("request")
|
|
500
|
+
params = req.get("workflow_params")
|
|
501
|
+
backend_params = req.get("workflow_backend_params")
|
|
501
502
|
else:
|
|
502
|
-
execs_path = f"{working_dir}/{execs}"
|
|
503
503
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
raise Exception(
|
|
522
|
-
f"Unable to generate workflow_params based on given exec_path, with error code {params}"
|
|
504
|
+
if not execs:
|
|
505
|
+
trs_register().load_resource().get_execs_json(
|
|
506
|
+
workflow_url=run.workflow_url, download_path=execs_path
|
|
507
|
+
)
|
|
508
|
+
else:
|
|
509
|
+
execs_path = f"{working_dir}/{execs}"
|
|
510
|
+
|
|
511
|
+
ops = resource.list_operator_pipelines(page=1, page_size=1000)["results"]
|
|
512
|
+
opp_w_args = [
|
|
513
|
+
op["id"]
|
|
514
|
+
for op in ops
|
|
515
|
+
for operator in op["operators"]
|
|
516
|
+
if isinstance(operator, dict) and operator.get("arguments")
|
|
517
|
+
]
|
|
518
|
+
params = parameters.workflow_params(
|
|
519
|
+
execs_path,
|
|
520
|
+
opp_w_args,
|
|
523
521
|
)
|
|
524
|
-
|
|
525
|
-
request = {
|
|
526
|
-
"name": run.run_name,
|
|
527
|
-
"workflow_params": params,
|
|
528
|
-
"workflow_backend_params": parameters.workflow_backend_params(
|
|
522
|
+
backend_params = parameters.workflow_backend_params(
|
|
529
523
|
execs_path,
|
|
530
524
|
workspace,
|
|
531
525
|
run.runtimes,
|
|
@@ -533,7 +527,22 @@ class BaseJobs:
|
|
|
533
527
|
trust,
|
|
534
528
|
kernel_version,
|
|
535
529
|
token_lifetime,
|
|
536
|
-
)
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
if is_runsheet_template:
|
|
533
|
+
params = parameters.runsheet_rendering(
|
|
534
|
+
params, run, fq_signature, is_single_end
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
if not isinstance(params, dict):
|
|
538
|
+
raise Exception(
|
|
539
|
+
f"Unable to generate workflow_params based on given exec_path, with error code {params}"
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
request = {
|
|
543
|
+
"name": run.run_name,
|
|
544
|
+
"workflow_params": params,
|
|
545
|
+
"workflow_backend_params": backend_params,
|
|
537
546
|
"workflow_url": run.workflow_url,
|
|
538
547
|
"workflow_type_version": "1.0",
|
|
539
548
|
"workflow_type": wf_info[1],
|