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.
- {seqslab-cli-3.3.3/python/seqslab_cli.egg-info → seqslab-cli-3.3.4}/PKG-INFO +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/azuread.py +2 -2
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/commands.py +4 -2
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/base.py +1 -28
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/commands.py +144 -26
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/parameters.py +11 -28
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/base.py +33 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/base.py +13 -10
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/SOURCES.txt +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/requires.txt +2 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/requirements.txt +2 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/LICENSE +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/MANIFEST.in +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/README.md +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/cli.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/aiocopy.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/utils.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/biomimetype.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/runsheet/runsheet.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/settings.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/user/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/azure.py +0 -0
- {seqslab-cli-3.3.3/python/seqslab/wes/internal → seqslab-cli-3.3.4/python/seqslab/wes/resource}/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/setup.cfg +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.4}/setup.py +0 -0
|
@@ -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/{
|
|
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(
|
|
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.
|
|
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 .
|
|
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
|
-
"
|
|
191
|
+
"request_path",
|
|
190
192
|
type=str,
|
|
191
193
|
positional=False,
|
|
192
|
-
description="Specify
|
|
194
|
+
description="Specify the request.json file to run (required).",
|
|
193
195
|
)
|
|
194
196
|
@argument(
|
|
195
|
-
"
|
|
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
|
-
|
|
205
|
+
aliases=["d"],
|
|
206
|
+
description="Specify the schedule date, in the format of YYYY-MM-DD (required).",
|
|
199
207
|
)
|
|
200
208
|
@argument(
|
|
201
|
-
"
|
|
209
|
+
"time",
|
|
202
210
|
type=str,
|
|
203
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
215
|
-
data=
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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,
|
|
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"
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|