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.
Files changed (111) hide show
  1. {seqslab-cli-3.3.6/python/seqslab_cli.egg-info → seqslab-cli-3.3.8}/PKG-INFO +1 -1
  2. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/__init__.py +1 -1
  3. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/commands.py +54 -15
  4. seqslab-cli-3.3.8/python/seqslab/auth/utils.py +46 -0
  5. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/commands.py +2 -49
  6. seqslab-cli-3.3.8/python/seqslab/organization/commands.py +117 -0
  7. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/runsheet/runsheet.py +11 -2
  8. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/commands.py +2 -3
  9. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/commands.py +41 -32
  10. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/internal/parameters.py +74 -59
  11. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/base.py +15 -0
  12. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/base.py +5 -1
  13. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
  14. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/SOURCES.txt +1 -0
  15. seqslab-cli-3.3.6/python/seqslab/organization/commands.py +0 -77
  16. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/LICENSE +0 -0
  17. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/MANIFEST.in +0 -0
  18. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/README.md +0 -0
  19. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/__init__.py +0 -0
  20. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/auth/azuread.py +0 -0
  21. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/cli.py +0 -0
  22. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/color.py +0 -0
  23. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/context.py +0 -0
  24. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/__init__.py +0 -0
  25. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/__init__.py +0 -0
  26. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/azure.py +0 -0
  27. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/base.py +0 -0
  28. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/common.py +0 -0
  29. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/api/template.py +0 -0
  30. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/__init__.py +0 -0
  31. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/aiocopy.py +0 -0
  32. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/common.py +0 -0
  33. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/internal/utils.py +0 -0
  34. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/__init__.py +0 -0
  35. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/azure.py +0 -0
  36. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/storage/base.py +0 -0
  37. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/__init__.py +0 -0
  38. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
  39. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/biomimetype.py +0 -0
  40. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/drs/utils/progressbar.py +0 -0
  41. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/exceptions.py +0 -0
  42. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/__init__.py +0 -0
  43. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/resource/__init__.py +0 -0
  44. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/organization/resource/base.py +0 -0
  45. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/plugin.py +0 -0
  46. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/__init__.py +0 -0
  47. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/commands.py +0 -0
  48. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/internal/__init__.py +0 -0
  49. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/internal/common.py +0 -0
  50. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/__init__.py +0 -0
  51. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/azure.py +0 -0
  52. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/role/resource/base.py +0 -0
  53. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/runsheet/__init__.py +0 -0
  54. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/__init__.py +0 -0
  55. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/_version.py +0 -0
  56. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/sample_sheet/util.py +0 -0
  57. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/__init__.py +0 -0
  58. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/commands.py +0 -0
  59. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/internal/__init__.py +0 -0
  60. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/internal/common.py +0 -0
  61. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/__init__.py +0 -0
  62. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/azure.py +0 -0
  63. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/scr/resource/base.py +0 -0
  64. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/session_logger.py +0 -0
  65. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/settings.py +0 -0
  66. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/statusbar.py +0 -0
  67. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/__init__.py +0 -0
  68. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/commands.py +0 -0
  69. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/internal/__init__.py +0 -0
  70. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/internal/utils.py +0 -0
  71. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/__init__.py +0 -0
  72. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/azure.py +0 -0
  73. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/base.py +0 -0
  74. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/common.py +0 -0
  75. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/register/template.py +0 -0
  76. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/__init__.py +0 -0
  77. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/azure.py +0 -0
  78. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/base.py +0 -0
  79. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/resource/common.py +0 -0
  80. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/__init__.py +0 -0
  81. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/base.py +0 -0
  82. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/trs/template/template.py +0 -0
  83. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/usage_logger.py +0 -0
  84. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/__init__.py +0 -0
  85. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/internal/__init__.py +0 -0
  86. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/internal/common.py +0 -0
  87. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/__init__.py +0 -0
  88. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/azure.py +0 -0
  89. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/user/resource/base.py +0 -0
  90. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/__init__.py +0 -0
  91. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/internal/__init__.py +0 -0
  92. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/__init__.py +0 -0
  93. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/azure.py +0 -0
  94. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/resource/common.py +0 -0
  95. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/__init__.py +0 -0
  96. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/wes/template/template.py +0 -0
  97. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/__init__.py +0 -0
  98. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/commands.py +0 -0
  99. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/internal/__init__.py +0 -0
  100. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/internal/common.py +0 -0
  101. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/__init__.py +0 -0
  102. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/azure.py +0 -0
  103. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab/workspace/resource/base.py +0 -0
  104. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
  105. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
  106. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/requires.txt +0 -0
  107. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/top_level.txt +0 -0
  108. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/python/seqslab_cli.egg-info/zip-safe +0 -0
  109. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/requirements.txt +0 -0
  110. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/setup.cfg +0 -0
  111. {seqslab-cli-3.3.6 → seqslab-cli-3.3.8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.6
3
+ Version: 3.3.8
4
4
  Summary: Atgenomix SeqsLab Command Line Tool
5
5
  Home-page: https://github.com/AnomeGAP/seqslab-cli
6
6
  Author: Allen Chang
@@ -21,6 +21,6 @@ name = "seqslab"
21
21
  __all__ = []
22
22
 
23
23
 
24
- __version__ = "3.3.6"
24
+ __version__ = "3.3.8"
25
25
 
26
26
  LOGGING = {"DIR_PATH": "/var/log/seqslab"}
@@ -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
- @staticmethod
49
- def _request(url, method, *args, **kwargs):
50
- response = request(method, url, **kwargs)
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 self._request(
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, device_code: bool, daemon: bool, backend: str, proxy: str = None
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
- self._request(
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 self._request(
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 Auth._request(
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
- def signin(self, device_code: bool = False, daemon: bool = False) -> int:
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
- sa.to_json().get("Add_Read2_ID") == ""
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, samples: List[Sample], run_name: str, workflow_url: str, runtimes: str
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 not name else email.split("@")[0],
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["name"] = name
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
- if not execs:
498
- trs_register().load_resource().get_execs_json(
499
- workflow_url=run.workflow_url, download_path=execs_path
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
- resource = get_factory().load_resource()
505
- ops = resource.list_operator_pipelines(page=1, page_size=1000)["results"]
506
- opp_w_args = [
507
- op["id"]
508
- for op in ops
509
- for operator in op["operators"]
510
- if isinstance(operator, dict) and operator.get("arguments")
511
- ]
512
- params = parameters.workflow_params(
513
- execs_path,
514
- run,
515
- is_runsheet_template,
516
- is_single_end,
517
- fq_signature,
518
- opp_w_args,
519
- )
520
- if not isinstance(params, dict):
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],