seqslab-cli 3.3.3__tar.gz → 3.3.4__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 (108) hide show
  1. {seqslab-cli-3.3.3/python/seqslab_cli.egg-info → seqslab-cli-3.3.4}/PKG-INFO +1 -1
  2. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/__init__.py +1 -1
  3. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/azuread.py +2 -2
  4. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/commands.py +4 -2
  5. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/base.py +1 -28
  6. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/commands.py +144 -26
  7. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/parameters.py +11 -28
  8. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/base.py +33 -0
  9. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/base.py +13 -10
  10. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
  11. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/SOURCES.txt +1 -1
  12. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/requires.txt +2 -0
  13. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/requirements.txt +2 -0
  14. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/LICENSE +0 -0
  15. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/MANIFEST.in +0 -0
  16. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/README.md +0 -0
  17. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/__init__.py +0 -0
  18. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/cli.py +0 -0
  19. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/context.py +0 -0
  20. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/__init__.py +0 -0
  21. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/__init__.py +0 -0
  22. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/azure.py +0 -0
  23. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/base.py +0 -0
  24. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/common.py +0 -0
  25. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/template.py +0 -0
  26. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/commands.py +0 -0
  27. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/__init__.py +0 -0
  28. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/aiocopy.py +0 -0
  29. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/common.py +0 -0
  30. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/utils.py +0 -0
  31. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/__init__.py +0 -0
  32. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/azure.py +0 -0
  33. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/base.py +0 -0
  34. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/__init__.py +0 -0
  35. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
  36. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/biomimetype.py +0 -0
  37. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/progressbar.py +0 -0
  38. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/exceptions.py +0 -0
  39. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/__init__.py +0 -0
  40. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/commands.py +0 -0
  41. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/__init__.py +0 -0
  42. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/base.py +0 -0
  43. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/plugin.py +0 -0
  44. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/__init__.py +0 -0
  45. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/commands.py +0 -0
  46. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/internal/__init__.py +0 -0
  47. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/internal/common.py +0 -0
  48. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/__init__.py +0 -0
  49. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/azure.py +0 -0
  50. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/base.py +0 -0
  51. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/runsheet/__init__.py +0 -0
  52. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/runsheet/runsheet.py +0 -0
  53. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/__init__.py +0 -0
  54. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/_version.py +0 -0
  55. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/util.py +0 -0
  56. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/__init__.py +0 -0
  57. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/commands.py +0 -0
  58. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/__init__.py +0 -0
  59. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/common.py +0 -0
  60. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/__init__.py +0 -0
  61. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/azure.py +0 -0
  62. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/base.py +0 -0
  63. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/session_logger.py +0 -0
  64. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/settings.py +0 -0
  65. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/statusbar.py +0 -0
  66. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/__init__.py +0 -0
  67. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/commands.py +0 -0
  68. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/__init__.py +0 -0
  69. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/utils.py +0 -0
  70. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/__init__.py +0 -0
  71. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/azure.py +0 -0
  72. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/base.py +0 -0
  73. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/common.py +0 -0
  74. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/template.py +0 -0
  75. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/__init__.py +0 -0
  76. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/azure.py +0 -0
  77. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/base.py +0 -0
  78. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/common.py +0 -0
  79. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/__init__.py +0 -0
  80. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/template.py +0 -0
  81. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/usage_logger.py +0 -0
  82. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/__init__.py +0 -0
  83. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/commands.py +0 -0
  84. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/internal/__init__.py +0 -0
  85. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/internal/common.py +0 -0
  86. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/__init__.py +0 -0
  87. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/azure.py +0 -0
  88. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/base.py +0 -0
  89. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/__init__.py +0 -0
  90. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/__init__.py +0 -0
  91. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/__init__.py +0 -0
  92. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/azure.py +0 -0
  93. {seqslab-cli-3.3.3/python/seqslab/wes/internal → seqslab-cli-3.3.4/python/seqslab/wes/resource}/common.py +0 -0
  94. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/__init__.py +0 -0
  95. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/template.py +0 -0
  96. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/__init__.py +0 -0
  97. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/commands.py +0 -0
  98. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/__init__.py +0 -0
  99. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/common.py +0 -0
  100. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/__init__.py +0 -0
  101. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/azure.py +0 -0
  102. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/base.py +0 -0
  103. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
  104. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
  105. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/top_level.txt +0 -0
  106. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/zip-safe +0 -0
  107. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/setup.cfg +0 -0
  108. {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.3
3
+ Version: 3.3.4
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.3"
24
+ __version__ = "3.3.4"
25
25
 
26
26
  LOGGING = {"DIR_PATH": "/var/log/seqslab"}
@@ -72,14 +72,14 @@ class ClientApp(AuthBaseModel):
72
72
  public_app: msal.ClientApplication = None
73
73
  confidential_app: msal.ClientApplication = None
74
74
 
75
- def load_client(self, credential: str = None) -> Client:
75
+ def load_client(self, credential: str = None, tenant: str = None) -> Client:
76
76
  proxies = context.get_context().args.proxy
77
77
  if credential:
78
78
  if not self.confidential_app:
79
79
  self.confidential_app = msal.ConfidentialClientApplication(
80
80
  SOCIAL_AUTH_AZURE_KEY,
81
81
  client_credential=credential,
82
- authority=f"https://login.microsoftonline.com/{SOCIAL_AUTH_AZURE_TENANT_ID}",
82
+ authority=f"https://login.microsoftonline.com/{tenant}",
83
83
  app_name=seqslab.name,
84
84
  app_version=seqslab.__version__,
85
85
  proxies=proxies,
@@ -60,7 +60,9 @@ class BaseAuth:
60
60
  proxy: str = None,
61
61
  ) -> dict:
62
62
  scopes = azuread.SOCIAL_AUTH_AZURE_SCOPE_APP.get(scope)
63
- client = aad_app.load_client(credential=credential)
63
+ client = aad_app.load_client(
64
+ credential=credential, tenant=self._decode(assertion)["tid"]
65
+ )
64
66
  result = client.acquire_token_silent(scopes, account=None)
65
67
  if not result:
66
68
  logging.info(
@@ -92,7 +94,7 @@ class BaseAuth:
92
94
  result = None
93
95
  token_uri = self.ACCESS_TOKEN_URL.format(backend=backend)
94
96
  scopes = azuread.SOCIAL_AUTH_AZURE_SCOPE
95
- client = aad_app.load_client()
97
+ client = aad_app.load_client(tenant=azuread.SOCIAL_AUTH_AZURE_TENANT_ID)
96
98
  accounts = client.get_accounts()
97
99
  if accounts:
98
100
  logging.info(
@@ -22,7 +22,7 @@ class TrsCreateTemplate:
22
22
  primary_descriptor=primary_descriptor, zip_file=zip_file
23
23
  )
24
24
  workflow = self.desc_template(parameter)
25
- calls = self.workflow_call_template(parameter)
25
+ calls = self.call_template(parameter)
26
26
  inputs_connections = self.inputs_connections(
27
27
  parameter=parameter, inputs_json=inputs_json
28
28
  )
@@ -41,7 +41,6 @@ class TrsCreateTemplate:
41
41
  @staticmethod
42
42
  def dot_cleaning(gs: str) -> str:
43
43
  # Standard Library
44
- import re
45
44
 
46
45
  rx = r';[\w;="]+'
47
46
  return re.sub(rx, "", gs)
@@ -59,32 +58,6 @@ class TrsCreateTemplate:
59
58
  calls.add(node.get_name().replace("CALL_", ""))
60
59
  return calls
61
60
 
62
- def workflow_call_template(self, parameter: dict) -> list:
63
- # adding main workflow
64
- runtime_cell_select = [
65
- pydot.graph_from_dot_data(
66
- TrsCreateTemplate.dot_cleaning(parameter["graph"])
67
- )[0].get_name()
68
- ]
69
-
70
- # parsing workflow>call information from graph string
71
- graphRex = re.compile(r'graph\s\w+|}|label="call\s\w+', re.IGNORECASE)
72
- wnames = []
73
- matches = graphRex.findall(parameter.get("graph"))
74
- if matches:
75
- for e in matches:
76
- if e.startswith("graph "):
77
- wnames.append(e.split(" ")[1].replace("cluster_", ""))
78
- elif e.startswith("}"):
79
- wnames.pop()
80
- elif e.startswith("label="):
81
- call = e.replace('label="', "").strip('"')
82
- if call.lower().startswith("call"):
83
- runtime_cell_select.append(
84
- "" + wnames[-1] + ">" + call.replace("call ", "")
85
- )
86
- return runtime_cell_select
87
-
88
61
  def call_template(self, parameter: dict) -> list:
89
62
  calls = [
90
63
  pydot.graph_from_dot_data(
@@ -11,6 +11,7 @@ from datetime import datetime
11
11
  from functools import lru_cache
12
12
  from typing import List
13
13
 
14
+ import pytz
14
15
  import requests
15
16
  from nubia import argument, command, context
16
17
  from requests_toolbelt.multipart.encoder import MultipartEncoder
@@ -22,8 +23,9 @@ from seqslab.wes import API_HOSTNAME, __version__
22
23
  from seqslab.wes.internal import parameters
23
24
  from tenacity import retry, stop_after_attempt, wait_fixed
24
25
  from termcolor import cprint
26
+ from tzlocal import get_localzone
25
27
 
26
- from .internal.common import get_factory
28
+ from .resource.common import get_factory
27
29
 
28
30
  """
29
31
  Copyright (C) 2022, Atgenomix Incorporated.
@@ -186,40 +188,122 @@ class BaseJobs:
186
188
 
187
189
  @command
188
190
  @argument(
189
- "run_request_id",
191
+ "request_path",
190
192
  type=str,
191
193
  positional=False,
192
- description="Specify a previously scheduled run request ID (required). ",
194
+ description="Specify the request.json file to run (required).",
193
195
  )
194
196
  @argument(
195
- "schedule_tag",
197
+ "workspace",
198
+ type=str,
199
+ description="Specify the workspace based on the signed in account (required).",
200
+ )
201
+ @argument(
202
+ "date",
196
203
  type=str,
197
204
  positional=False,
198
- description="Specify a tag marked on previously uploaded samples for a scheduled WES run (required).",
205
+ aliases=["d"],
206
+ description="Specify the schedule date, in the format of YYYY-MM-DD (required).",
199
207
  )
200
208
  @argument(
201
- "workspace",
209
+ "time",
202
210
  type=str,
203
- description="Specify the workspace based on the signed in account (required).",
211
+ positional=False,
212
+ aliases=["t"],
213
+ description="Specify the schedule time, in the format of HH-MM (required).",
214
+ )
215
+ @argument(
216
+ "time_zone",
217
+ type=str,
218
+ choices=["UTC", "LOCAL"],
219
+ positional=False,
220
+ aliases=["z"],
221
+ description="Specify the time zone for the provided date and time. If 'LOCAL' is selected, the date and time "
222
+ "will be interpreted according to the operating system's time zone. (optional, default = UTC).",
223
+ )
224
+ @argument(
225
+ "recurrence",
226
+ type=str,
227
+ positional=False,
228
+ aliases=["r"],
229
+ choices=["Once", "Hourly", "Daily", "Weekly", "Monthly"],
230
+ description="Specify the schedule recurrence (optional, default = Once).",
204
231
  )
205
- def schedule(self, run_request_id: str, schedule_tag: str, workspace: str) -> int:
232
+ def schedule(
233
+ self,
234
+ request_path: str,
235
+ workspace: str,
236
+ date: str,
237
+ time: str,
238
+ time_zone: str = "UTC",
239
+ recurrence: str = "Once",
240
+ ) -> int:
206
241
  """
207
- Run a WES run based on a previously registered run request. Typically, the run request
208
- is designed for a scheduled WES run, where the FQN-DRS connection of sequencing samples
209
- are left blank for future runtime sample-resolving. Thus, by specifying a sample-resolving rule,
210
- the run request can be used to serve a scheduled WES run use case.
242
+ Schedule a WES run with given date, time, and recurrence.
211
243
  """
212
- mp = MultipartEncoder(fields={})
244
+ if not os.path.isfile(request_path):
245
+ cprint("request_path is not a file", "red")
246
+ return errno.EINVAL
247
+ else:
248
+ try:
249
+ with open(request_path, "r") as f:
250
+ req = json.load(f)
251
+ except json.decoder.JSONDecodeError as e:
252
+ cprint(f"given request not in json format - {e}", "red")
253
+ return errno.EINVAL
254
+
255
+ if not self._is_valid_time(date, "%Y-%m-%d"):
256
+ cprint(f"date {date} not in YYYY-MM-DD format", "red")
257
+ return errno.EINVAL
258
+
259
+ if not self._is_valid_time(time, "%H:%M"):
260
+ cprint(f"time {time} not in HH-MM format", "red")
261
+ return errno.EINVAL
262
+
263
+ time_f = f"{date} {time}"
264
+
265
+ if time_zone == "LOCAL":
266
+ time_f, msg = self._to_utc(time_f)
267
+ if not time_f:
268
+ cprint(f"timezone transform failed {msg}", "red")
269
+ return errno.EINVAL
270
+
271
+ payloads = {
272
+ "schedule": {"schedule_type": recurrence[0], "next_run": time_f},
273
+ "request": req,
274
+ }
275
+
213
276
  resource = get_factory().load_resource(workspace)
214
- ret = resource.sync_run_jobs(
215
- data=mp,
216
- headers={"Content-Type": mp.content_type},
217
- run_request_id=run_request_id,
218
- run_name=schedule_tag,
277
+ ret = resource.schedule_run(
278
+ data=payloads,
279
+ ).json()
280
+ cprint(
281
+ f"wes_schedule_id: {ret['id']}; schedule_details: {str(ret['schedule'])}",
282
+ "yellow",
219
283
  )
220
- cprint(f"{ret.content.decode('utf-8')}", "yellow")
221
284
  return 0
222
285
 
286
+ @staticmethod
287
+ def _is_valid_time(time_str: str, time_format: str):
288
+ try:
289
+ datetime.strptime(time_str, time_format)
290
+ return True
291
+ except ValueError:
292
+ return False
293
+
294
+ @staticmethod
295
+ def _to_utc(local_t: str):
296
+ try:
297
+ local_timezone = pytz.timezone(get_localzone().key)
298
+ utc_t = (
299
+ local_timezone.localize(datetime.strptime(local_t, "%Y-%m-%d %H:%M"))
300
+ .astimezone(pytz.utc)
301
+ .strftime("%Y-%m-%d %H:%M")
302
+ )
303
+ return utc_t, None
304
+ except Exception as e:
305
+ return None, str(e)
306
+
223
307
  @command(aliases=["state"])
224
308
  @argument(
225
309
  "run_id",
@@ -400,8 +484,21 @@ class BaseJobs:
400
484
  else:
401
485
  execs_path = f"{working_dir}/{execs}"
402
486
 
487
+ resource = get_factory().load_resource(workspace)
488
+ ops = resource.list_operator_pipelines(page=1, page_size=1000)["results"]
489
+ opp_w_args = [
490
+ op["id"]
491
+ for op in ops
492
+ for operator in op["operators"]
493
+ if isinstance(operator, dict) and operator.get("arguments")
494
+ ]
403
495
  params = parameters.workflow_params(
404
- execs_path, run, is_runsheet_template, is_single_end, fq_signature
496
+ execs_path,
497
+ run,
498
+ is_runsheet_template,
499
+ is_single_end,
500
+ fq_signature,
501
+ opp_w_args,
405
502
  )
406
503
  if not isinstance(params, dict):
407
504
  raise Exception(
@@ -620,10 +717,9 @@ class BaseJobs:
620
717
  try:
621
718
  result = get_factory().load_resource(workspace).cancel_run(run_id)
622
719
  cprint(json.dumps(result, indent=4), "yellow")
623
- except requests.HTTPError:
624
- cprint(f"given run_id {run_id} is not valid.", "red")
720
+ except requests.HTTPError as e:
721
+ cprint(f"Fail to cancel Job {run_id} - '{str(e)}'.", "red")
625
722
  return -1
626
-
627
723
  return 0
628
724
 
629
725
  @command
@@ -725,7 +821,7 @@ class BaseJobs:
725
821
  List registered operator pipelines.
726
822
  """
727
823
  resource = get_factory().load_resource(workspace)
728
- r = resource.list_runtime_options(page=page, page_size=page_size)
824
+ r = resource.list_operator_pipelines(page=page, page_size=page_size)
729
825
 
730
826
  if isinstance(r, int):
731
827
  return r
@@ -736,8 +832,6 @@ class BaseJobs:
736
832
  @staticmethod
737
833
  def _stdout(results, output: str) -> int:
738
834
  # Standard Library
739
- import csv
740
- from io import StringIO
741
835
 
742
836
  from tabulate import tabulate
743
837
 
@@ -756,6 +850,30 @@ class BaseJobs:
756
850
  )
757
851
  return 0
758
852
 
853
+ @command
854
+ @argument(
855
+ "run_id",
856
+ type=str,
857
+ positional=False,
858
+ description="Specify the run_id that is going to be delete (required).",
859
+ )
860
+ @argument(
861
+ "workspace",
862
+ type=str,
863
+ description="Specify the workspace based on the signed in account (required).",
864
+ )
865
+ def delete(self, run_id: str, workspace: str) -> int:
866
+ """
867
+ Delete WES run as well as all the generated output files based on run ID.
868
+ """
869
+ try:
870
+ get_factory().load_resource(workspace).delete_run(run_id)
871
+ except requests.HTTPError as e:
872
+ cprint(f"Fail to delete Job {run_id} - '{str(e)}'.", "red")
873
+ return -1
874
+
875
+ return 0
876
+
759
877
 
760
878
  @command
761
879
  class Jobs(BaseJobs):
@@ -6,7 +6,7 @@ import re
6
6
  import zipfile
7
7
 
8
8
  from seqslab.runsheet.runsheet import Run
9
- from seqslab.wes.internal.common import get_factory
9
+ from seqslab.wes.resource.common import get_factory
10
10
  from seqslab.wes.template.base import (
11
11
  WorkflowBackendParamsClusterTemplate,
12
12
  WorkflowBackendParamsTemplate,
@@ -119,6 +119,7 @@ def workflow_params(
119
119
  is_runsheet_template: bool,
120
120
  is_single_end: bool,
121
121
  fq_sig: str,
122
+ opp_w_args: dict,
122
123
  ) -> dict:
123
124
  """
124
125
  Create workflow_params.json.
@@ -132,7 +133,9 @@ def workflow_params(
132
133
  with open(execs_json, "r") as f:
133
134
  t_content = json.loads(f.read())
134
135
 
135
- params = WorkflowParamsTemplate().create(ex_template=t_content)
136
+ params = WorkflowParamsTemplate().create(
137
+ ex_template=t_content, opp_w_args=opp_w_args
138
+ )
136
139
 
137
140
  if is_runsheet_template:
138
141
  r1fqn, r2fqn = validate_label_column(run)
@@ -230,33 +233,13 @@ def workflow_backend_params(
230
233
  raise RuntimeError(
231
234
  f"given call name {k} not in TRS registered call name list {calls}!"
232
235
  )
233
-
234
- # general workflow>call handling
235
- ids = k.split(">")
236
- if len(ids) == 2:
237
- clusters.append(
238
- WorkflowBackendParamsClusterTemplate(
239
- run_time=resource.get_runtime_setting(v),
240
- call_name=ids[1],
241
- workflow_name=ids[0],
242
- kernel_version=kernel_version,
243
- )
236
+ clusters.append(
237
+ WorkflowBackendParamsClusterTemplate(
238
+ run_time=resource.get_runtime_setting(v),
239
+ workflow_name=k,
240
+ kernel_version=kernel_version,
244
241
  )
245
- else:
246
- # main workflow name
247
- if k == primary_workflow_name:
248
- clusters.append(
249
- WorkflowBackendParamsClusterTemplate(
250
- run_time=resource.get_runtime_setting(v),
251
- call_name=k,
252
- kernel_version=kernel_version,
253
- )
254
- )
255
- else:
256
- # raise exception for incorrect format of k
257
- raise RuntimeError(
258
- f"given identifier {k} does not in format of in TRS registered call name list {calls}!"
259
- )
242
+ )
260
243
 
261
244
  bk_template = WorkflowBackendParamsTemplate(
262
245
  graph=execs.get("graph"),
@@ -55,6 +55,7 @@ class BaseResource(ABC):
55
55
  WES_RUNTIME_OPTIONS_URL = (
56
56
  f"{WES_BASE_URL}runtime-options/{{name}}?backend={{backend}}"
57
57
  )
58
+ WES_SCHEDULES_URL = f"{WES_BASE_URL}schedules/?backend={{backend}}"
58
59
 
59
60
  class Response(NamedTuple):
60
61
  status: int
@@ -313,6 +314,16 @@ class BaseResource(ABC):
313
314
  raise requests.HTTPError(response.text)
314
315
  return json.loads(response.content)
315
316
 
317
+ def delete_run(self, run_id) -> int:
318
+ token = BaseAuth.get_token().get("tokens").get("access")
319
+ with requests.delete(
320
+ url=f"{self.WES_BASE_URL}runs/{run_id}",
321
+ headers={"Authorization": f"Bearer {token}"},
322
+ ) as response:
323
+ if response.status_code not in [204]:
324
+ raise requests.HTTPError(response.text)
325
+ return 0
326
+
316
327
  def list_runtime_options(self, page=1, page_size=10):
317
328
  try:
318
329
  token = BaseAuth.get_token().get("tokens").get("access")
@@ -344,3 +355,25 @@ class BaseResource(ABC):
344
355
  print(err)
345
356
  raise err
346
357
  return response.json()
358
+
359
+ def schedule_run(self, data) -> requests.Response:
360
+ try:
361
+ ctx = context.get_context()
362
+ backend = ctx.args.backend
363
+ token = BaseAuth.get_token().get("tokens").get("access")
364
+
365
+ response = requests.post(
366
+ url=self.WES_SCHEDULES_URL.format(backend=backend),
367
+ headers={"Authorization": f"Bearer {token}"},
368
+ json=data,
369
+ )
370
+
371
+ if response.status_code not in [requests.codes.created]:
372
+ raise requests.HTTPError(
373
+ f"{response.status_code}: {repr(response.text)}"
374
+ )
375
+ except Exception as err:
376
+ print(err)
377
+ raise err
378
+
379
+ return response
@@ -1,6 +1,6 @@
1
1
  class WorkflowParamsTemplate:
2
- def create(self, ex_template: dict) -> dict:
3
- operator_pipelines = self.operator_pipelines(ex_template)
2
+ def create(self, ex_template: dict, opp_w_args: dict) -> dict:
3
+ operator_pipelines = self.operator_pipelines(ex_template, opp_w_args)
4
4
  return {
5
5
  "inputs": ex_template.get("inputs"),
6
6
  "datasets": ex_template.get("connections", None),
@@ -19,8 +19,7 @@ class WorkflowParamsTemplate:
19
19
  return self._flat_list(sub_v, r, layer)
20
20
  return {"list": r, "layer": layer}
21
21
 
22
- @staticmethod
23
- def operator_pipelines(ex_template: dict) -> dict:
22
+ def operator_pipelines(self, ex_template: dict, opp_w_args: dict) -> dict:
24
23
  """
25
24
  :param: parameter: parameter API response
26
25
  :return:
@@ -42,13 +41,20 @@ class WorkflowParamsTemplate:
42
41
  for pipeline in ex_template["operator_pipelines"]:
43
42
  if pipeline["id"] == v:
44
43
  tasks[k] = {
45
- "id": v,
44
+ "id": self.norm_pl_key(v, opp_w_args),
46
45
  "operators": pipeline["operators"],
47
46
  "description": pipeline["description"],
48
47
  }
49
48
  continue
50
49
  return tasks
51
50
 
51
+ @staticmethod
52
+ def norm_pl_key(pl_key: str, opp_w_args: dict) -> str:
53
+ for opp in opp_w_args:
54
+ if pl_key.startswith(opp):
55
+ return opp
56
+ return pl_key
57
+
52
58
  @staticmethod
53
59
  def inputs_connections(inputs_connection: list = None) -> dict:
54
60
  """
@@ -115,14 +121,11 @@ def WorkflowBackendParamsTemplate(
115
121
 
116
122
 
117
123
  def WorkflowBackendParamsClusterTemplate(
118
- run_time: dict, call_name: str, kernel_version: str, workflow_name: str = ""
124
+ run_time: dict, kernel_version: str, workflow_name: str = ""
119
125
  ) -> dict:
120
126
  if kernel_version:
121
127
  opts = run_time["options"]
122
128
  opts.append(f"seqslab.kernel.version {kernel_version}")
123
129
  run_time.update({"options": opts})
124
- if workflow_name:
125
- run_time.update({"call": call_name, "workflow": workflow_name})
126
- else:
127
- run_time.update({"call": call_name})
130
+ run_time.update({"call": workflow_name})
128
131
  return run_time
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.3
3
+ Version: 3.3.4
4
4
  Summary: Atgenomix SeqsLab Command Line Tool
5
5
  Home-page: https://github.com/AnomeGAP/seqslab-cli
6
6
  Author: Allen Chang
@@ -83,11 +83,11 @@ python/seqslab/user/resource/base.py
83
83
  python/seqslab/wes/__init__.py
84
84
  python/seqslab/wes/commands.py
85
85
  python/seqslab/wes/internal/__init__.py
86
- python/seqslab/wes/internal/common.py
87
86
  python/seqslab/wes/internal/parameters.py
88
87
  python/seqslab/wes/resource/__init__.py
89
88
  python/seqslab/wes/resource/azure.py
90
89
  python/seqslab/wes/resource/base.py
90
+ python/seqslab/wes/resource/common.py
91
91
  python/seqslab/wes/template/__init__.py
92
92
  python/seqslab/wes/template/base.py
93
93
  python/seqslab/wes/template/template.py
@@ -28,3 +28,5 @@ aiohttp_retry==2.8.3
28
28
  requests~=2.31.0
29
29
  setuptools~=69.5.1
30
30
  jsonpath-ng==1.6.0
31
+ tzlocal==5.2
32
+ pytz==2021.3
@@ -24,3 +24,5 @@ aiohttp_retry==2.8.3
24
24
  requests~=2.31.0
25
25
  setuptools~=69.5.1
26
26
  jsonpath-ng==1.6.0
27
+ tzlocal==5.2
28
+ pytz==2021.3
File without changes
File without changes
File without changes
File without changes
File without changes