seqslab-cli 3.3.2.post1__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.2.post1/python/seqslab_cli.egg-info → seqslab-cli-3.3.4}/PKG-INFO +1 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/auth/azuread.py +2 -2
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/auth/commands.py +4 -2
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/api/azure.py +12 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/api/base.py +2 -2
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/commands.py +3 -3
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/template/base.py +1 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/resource/base.py +3 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/commands.py +241 -28
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/parameters.py +5 -2
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/base.py +67 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/template/base.py +14 -6
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/SOURCES.txt +1 -1
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/requires.txt +7 -5
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/requirements.txt +7 -5
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/LICENSE +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/MANIFEST.in +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/README.md +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/cli.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/aiocopy.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/internal/utils.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/biomimetype.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/organization/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/runsheet/runsheet.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/internal/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/scr/resource/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/settings.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/register/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/resource/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/template/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/user/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1/python/seqslab/wes/internal → seqslab-cli-3.3.4/python/seqslab/wes/resource}/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/template/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/commands.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/__init__.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/azure.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/workspace/resource/base.py +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/setup.cfg +0 -0
- {seqslab-cli-3.3.2.post1 → 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(
|
|
@@ -8,6 +8,7 @@ from typing import List
|
|
|
8
8
|
import requests
|
|
9
9
|
from nubia import context
|
|
10
10
|
from seqslab.auth.commands import BaseAuth
|
|
11
|
+
from seqslab.drs.storage.azure import BlobStorage
|
|
11
12
|
from seqslab.drs.utils.biomimetype import get_mime_type
|
|
12
13
|
from tenacity import retry, stop_after_attempt, wait_fixed
|
|
13
14
|
from termcolor import cprint
|
|
@@ -396,3 +397,14 @@ class AzureDRSregister(DRSregister):
|
|
|
396
397
|
)
|
|
397
398
|
|
|
398
399
|
return dsts_list
|
|
400
|
+
|
|
401
|
+
def common_root(self, dsts: List[URL], **kwargs) -> str:
|
|
402
|
+
root_dst = super().common_root(dsts)
|
|
403
|
+
blob_root = BlobStorage(kwargs.get("workspace")).get_token(path=None)[
|
|
404
|
+
"register_url"
|
|
405
|
+
]
|
|
406
|
+
if URL(root_dst).path == URL(blob_root).path:
|
|
407
|
+
raise ValueError(
|
|
408
|
+
"access_url pointing to cloud storage root path is not allowed"
|
|
409
|
+
)
|
|
410
|
+
return root_dst
|
|
@@ -335,7 +335,7 @@ class DRSregister:
|
|
|
335
335
|
folder_stdin["access_methods"].append(access_method_infos[i])
|
|
336
336
|
folder_stdin["access_methods"][i]["access_url"][
|
|
337
337
|
"url"
|
|
338
|
-
] = self.common_root(dsts=dsts)
|
|
338
|
+
] = self.common_root(dsts=dsts, workspace=kwargs.get("workspace"))
|
|
339
339
|
|
|
340
340
|
folder_stdin["name"] = kwargs.get(
|
|
341
341
|
"name",
|
|
@@ -360,7 +360,7 @@ class DRSregister:
|
|
|
360
360
|
raise ValueError("Make sure all the sizes are filled.")
|
|
361
361
|
return sum(sizes)
|
|
362
362
|
|
|
363
|
-
def common_root(self, dsts: List[URL]) -> str:
|
|
363
|
+
def common_root(self, dsts: List[URL], **kwargs) -> str:
|
|
364
364
|
"""
|
|
365
365
|
e.g. http://user:password@example.com:8042/over/there?name=ferret#nose
|
|
366
366
|
URL.scheme: http
|
|
@@ -173,7 +173,7 @@ class BaseDatahub:
|
|
|
173
173
|
"dst",
|
|
174
174
|
type=str,
|
|
175
175
|
positional=False,
|
|
176
|
-
description="Specify the destination directory, file path, or DRS URL (optional, default ='
|
|
176
|
+
description="Specify the destination directory, file path, or DRS URL (optional, default ='MyDir/'). ",
|
|
177
177
|
)
|
|
178
178
|
@argument(
|
|
179
179
|
"recursive",
|
|
@@ -223,7 +223,7 @@ class BaseDatahub:
|
|
|
223
223
|
self,
|
|
224
224
|
workspace: str,
|
|
225
225
|
src: str,
|
|
226
|
-
dst: str = "/",
|
|
226
|
+
dst: str = "MyDir/",
|
|
227
227
|
recursive: bool = False,
|
|
228
228
|
output: str = "json",
|
|
229
229
|
concurrency: int = 0,
|
|
@@ -706,7 +706,7 @@ class BaseDatahub:
|
|
|
706
706
|
backend = drs_register().load_register(workspace)
|
|
707
707
|
if stdin:
|
|
708
708
|
payloads = backend.create_payload(
|
|
709
|
-
stdin=BaseDatahub._stdin(), type=drs_type, **kwargs
|
|
709
|
+
stdin=BaseDatahub._stdin(), type=drs_type, workspace=workspace, **kwargs
|
|
710
710
|
)
|
|
711
711
|
else:
|
|
712
712
|
if not kwargs["is_integrity"]:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Standard Library
|
|
2
|
+
import re
|
|
2
3
|
from typing import List
|
|
3
4
|
|
|
4
5
|
import pydot
|
|
@@ -40,7 +41,6 @@ class TrsCreateTemplate:
|
|
|
40
41
|
@staticmethod
|
|
41
42
|
def dot_cleaning(gs: str) -> str:
|
|
42
43
|
# Standard Library
|
|
43
|
-
import re
|
|
44
44
|
|
|
45
45
|
rx = r';[\w;="]+'
|
|
46
46
|
return re.sub(rx, "", gs)
|
|
@@ -112,7 +112,9 @@ class BaseResource:
|
|
|
112
112
|
def is_global_admin(self, **kwargs) -> bool:
|
|
113
113
|
token = BaseAuth.get_token()
|
|
114
114
|
response = self.get_user(token.get("attrs").get("user_id"))
|
|
115
|
-
if "Global administrator" in
|
|
115
|
+
if "Global administrator" in [
|
|
116
|
+
item.get("name") for item in response.get("roles")
|
|
117
|
+
]:
|
|
116
118
|
return True
|
|
117
119
|
|
|
118
120
|
return False
|
|
@@ -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).",
|
|
195
|
+
)
|
|
196
|
+
@argument(
|
|
197
|
+
"workspace",
|
|
198
|
+
type=str,
|
|
199
|
+
description="Specify the workspace based on the signed in account (required).",
|
|
193
200
|
)
|
|
194
201
|
@argument(
|
|
195
|
-
"
|
|
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).",
|
|
204
223
|
)
|
|
205
|
-
|
|
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).",
|
|
231
|
+
)
|
|
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(
|
|
@@ -458,11 +555,11 @@ class BaseJobs:
|
|
|
458
555
|
@argument(
|
|
459
556
|
"runtimes",
|
|
460
557
|
type=str,
|
|
461
|
-
description="
|
|
462
|
-
"
|
|
463
|
-
"
|
|
464
|
-
"
|
|
465
|
-
"
|
|
558
|
+
description="String of key-value pairs using : as a separator, indicating the execution runtimes for each task "
|
|
559
|
+
"or workflow defined in workflow-url. For example, Main=m4-cluster:Main>Fq2Bam=m4-8xcluster. To "
|
|
560
|
+
"list tasks/workflows for a given workflow, use the execs command. To find available runtime "
|
|
561
|
+
"options, use the request runtimes_options command. (Optional, defaults to None, which runs the "
|
|
562
|
+
"entire workflow using m4-cluster.)",
|
|
466
563
|
)
|
|
467
564
|
@argument(
|
|
468
565
|
"integrity",
|
|
@@ -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
|
|
@@ -661,6 +757,123 @@ class BaseJobs:
|
|
|
661
757
|
|
|
662
758
|
return 0
|
|
663
759
|
|
|
760
|
+
@command(aliases=["rt"])
|
|
761
|
+
@argument(
|
|
762
|
+
"page",
|
|
763
|
+
type=int,
|
|
764
|
+
positional=False,
|
|
765
|
+
description="Specify the page number in the set of paginated records (optional, default = 1).",
|
|
766
|
+
)
|
|
767
|
+
@argument(
|
|
768
|
+
"page_size",
|
|
769
|
+
type=int,
|
|
770
|
+
positional=False,
|
|
771
|
+
description="Specify the number of records to return in each page (optional, default = 10).",
|
|
772
|
+
)
|
|
773
|
+
@command
|
|
774
|
+
@argument(
|
|
775
|
+
"output",
|
|
776
|
+
type=str,
|
|
777
|
+
positional=False,
|
|
778
|
+
description="Specify the output format of the stdout file (optional, default = json).",
|
|
779
|
+
choices=["json", "table"],
|
|
780
|
+
)
|
|
781
|
+
def runtime_options(
|
|
782
|
+
self, workspace: str, page: int = 1, page_size: int = 10, output="json"
|
|
783
|
+
) -> int:
|
|
784
|
+
"""
|
|
785
|
+
List registered cluster runtimes settings.
|
|
786
|
+
"""
|
|
787
|
+
resource = get_factory().load_resource(workspace)
|
|
788
|
+
r = resource.list_runtime_options(page=page, page_size=page_size)
|
|
789
|
+
|
|
790
|
+
if isinstance(r, int):
|
|
791
|
+
return r
|
|
792
|
+
|
|
793
|
+
self._stdout(r["results"], output)
|
|
794
|
+
return 0
|
|
795
|
+
|
|
796
|
+
@command(aliases=["op"])
|
|
797
|
+
@argument(
|
|
798
|
+
"page",
|
|
799
|
+
type=int,
|
|
800
|
+
positional=False,
|
|
801
|
+
description="Specify the page number in the set of paginated records (optional, default = 1).",
|
|
802
|
+
)
|
|
803
|
+
@argument(
|
|
804
|
+
"page_size",
|
|
805
|
+
type=int,
|
|
806
|
+
positional=False,
|
|
807
|
+
description="Specify the number of records to return in each page (optional, default = 10).",
|
|
808
|
+
)
|
|
809
|
+
@command
|
|
810
|
+
@argument(
|
|
811
|
+
"output",
|
|
812
|
+
type=str,
|
|
813
|
+
positional=False,
|
|
814
|
+
description="Specify the output format of the stdout file (optional, default = json).",
|
|
815
|
+
choices=["json", "table"],
|
|
816
|
+
)
|
|
817
|
+
def operator_pipelines(
|
|
818
|
+
self, workspace: str, page: int = 1, page_size: int = 10, output="json"
|
|
819
|
+
) -> int:
|
|
820
|
+
"""
|
|
821
|
+
List registered operator pipelines.
|
|
822
|
+
"""
|
|
823
|
+
resource = get_factory().load_resource(workspace)
|
|
824
|
+
r = resource.list_operator_pipelines(page=page, page_size=page_size)
|
|
825
|
+
|
|
826
|
+
if isinstance(r, int):
|
|
827
|
+
return r
|
|
828
|
+
|
|
829
|
+
self._stdout(r["results"], output)
|
|
830
|
+
return 0
|
|
831
|
+
|
|
832
|
+
@staticmethod
|
|
833
|
+
def _stdout(results, output: str) -> int:
|
|
834
|
+
# Standard Library
|
|
835
|
+
|
|
836
|
+
from tabulate import tabulate
|
|
837
|
+
|
|
838
|
+
"""
|
|
839
|
+
stdout:: TODO: support different format ex: json, tsv, table
|
|
840
|
+
"""
|
|
841
|
+
if output == "json":
|
|
842
|
+
cprint(json.dumps(results, indent=4))
|
|
843
|
+
elif output == "table":
|
|
844
|
+
table_header = list(results[0].keys())
|
|
845
|
+
table_datas = [result.values() for result in results]
|
|
846
|
+
cprint(
|
|
847
|
+
tabulate(
|
|
848
|
+
tabular_data=table_datas, headers=table_header, tablefmt="pipe"
|
|
849
|
+
)
|
|
850
|
+
)
|
|
851
|
+
return 0
|
|
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
|
+
|
|
664
877
|
|
|
665
878
|
@command
|
|
666
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)
|
|
@@ -50,9 +50,12 @@ class BaseResource(ABC):
|
|
|
50
50
|
WES_RUNS_DRY_URL = f"{WES_BASE_URL}runs/dryrun/?backend={{backend}}"
|
|
51
51
|
WES_RUNS_FILE_URL = f"{WES_BASE_URL}runs/{{id}}/files/?backend={{backend}}"
|
|
52
52
|
WES_RUNS_STATUS_URL = f"{WES_BASE_URL}runs/{{id}}/status/?backend={{backend}}"
|
|
53
|
+
WES_RUNTIME_OPTIONS_BASE_URL = f"{WES_BASE_URL}runtime-options/"
|
|
54
|
+
WES_OPERATOR_PIPELINES_BASE_URL = f"{WES_BASE_URL}operator-pipelines/"
|
|
53
55
|
WES_RUNTIME_OPTIONS_URL = (
|
|
54
56
|
f"{WES_BASE_URL}runtime-options/{{name}}?backend={{backend}}"
|
|
55
57
|
)
|
|
58
|
+
WES_SCHEDULES_URL = f"{WES_BASE_URL}schedules/?backend={{backend}}"
|
|
56
59
|
|
|
57
60
|
class Response(NamedTuple):
|
|
58
61
|
status: int
|
|
@@ -310,3 +313,67 @@ class BaseResource(ABC):
|
|
|
310
313
|
if response.status_code not in [requests.codes.ok]:
|
|
311
314
|
raise requests.HTTPError(response.text)
|
|
312
315
|
return json.loads(response.content)
|
|
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
|
+
|
|
327
|
+
def list_runtime_options(self, page=1, page_size=10):
|
|
328
|
+
try:
|
|
329
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
330
|
+
with requests.get(
|
|
331
|
+
url=f"{self.WES_RUNTIME_OPTIONS_BASE_URL}?page={page}&page_size={page_size}",
|
|
332
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
333
|
+
) as response:
|
|
334
|
+
if response.status_code not in [requests.codes.ok]:
|
|
335
|
+
raise requests.HTTPError(
|
|
336
|
+
f"{response.status_code}: {repr(response.text)}"
|
|
337
|
+
)
|
|
338
|
+
except Exception as err:
|
|
339
|
+
print(err)
|
|
340
|
+
raise err
|
|
341
|
+
return response.json()
|
|
342
|
+
|
|
343
|
+
def list_operator_pipelines(self, page=1, page_size=10):
|
|
344
|
+
try:
|
|
345
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
346
|
+
with requests.get(
|
|
347
|
+
url=f"{self.WES_OPERATOR_PIPELINES_BASE_URL}?page={page}&page_size={page_size}",
|
|
348
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
349
|
+
) as response:
|
|
350
|
+
if response.status_code not in [requests.codes.ok]:
|
|
351
|
+
raise requests.HTTPError(
|
|
352
|
+
f"{response.status_code}: {repr(response.text)}"
|
|
353
|
+
)
|
|
354
|
+
except Exception as err:
|
|
355
|
+
print(err)
|
|
356
|
+
raise err
|
|
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:
|
|
@@ -34,19 +33,28 @@ class WorkflowParamsTemplate:
|
|
|
34
33
|
tasks = {}
|
|
35
34
|
pl_keys = [pipeline["id"] for pipeline in ex_template["operator_pipelines"]]
|
|
36
35
|
for k, v in dict(ex_template["i_configs"], **ex_template["o_configs"]).items():
|
|
36
|
+
if not v:
|
|
37
|
+
continue
|
|
37
38
|
assert (
|
|
38
39
|
v in pl_keys
|
|
39
40
|
), f"given operator pipeline ID {v} for FQN {k} not in operator pipeline list from execs: {pl_keys}"
|
|
40
41
|
for pipeline in ex_template["operator_pipelines"]:
|
|
41
42
|
if pipeline["id"] == v:
|
|
42
43
|
tasks[k] = {
|
|
43
|
-
"id": v,
|
|
44
|
+
"id": self.norm_pl_key(v, opp_w_args),
|
|
44
45
|
"operators": pipeline["operators"],
|
|
45
46
|
"description": pipeline["description"],
|
|
46
47
|
}
|
|
47
48
|
continue
|
|
48
49
|
return tasks
|
|
49
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
|
+
|
|
50
58
|
@staticmethod
|
|
51
59
|
def inputs_connections(inputs_connection: list = None) -> dict:
|
|
52
60
|
"""
|
|
@@ -113,7 +121,7 @@ def WorkflowBackendParamsTemplate(
|
|
|
113
121
|
|
|
114
122
|
|
|
115
123
|
def WorkflowBackendParamsClusterTemplate(
|
|
116
|
-
run_time: dict,
|
|
124
|
+
run_time: dict, kernel_version: str, workflow_name: str = ""
|
|
117
125
|
) -> dict:
|
|
118
126
|
if kernel_version:
|
|
119
127
|
opts = run_time["options"]
|
|
@@ -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
|
|
@@ -2,7 +2,7 @@ pip>=22.0.4
|
|
|
2
2
|
click==8.1.7
|
|
3
3
|
tabulate==0.9.0
|
|
4
4
|
terminaltables==3.1.10
|
|
5
|
-
cryptography==
|
|
5
|
+
cryptography==42.0.6
|
|
6
6
|
jeepney==0.8.0
|
|
7
7
|
secretstorage==3.3.3
|
|
8
8
|
dbus-python==1.2.16
|
|
@@ -12,7 +12,7 @@ python-nubia==0.2b5
|
|
|
12
12
|
msal==1.26.0
|
|
13
13
|
tenacity==8.2.3
|
|
14
14
|
aiofiles==23.2.1
|
|
15
|
-
aiohttp[speedups]==3.9.
|
|
15
|
+
aiohttp[speedups]==3.9.5
|
|
16
16
|
aioretry==5.0.2
|
|
17
17
|
uvloop==0.19.0
|
|
18
18
|
arrow==1.3.0
|
|
@@ -22,9 +22,11 @@ PyJWT==2.8.0
|
|
|
22
22
|
django-environ==0.11.2
|
|
23
23
|
validators==0.22.0
|
|
24
24
|
pydot==2.0.0
|
|
25
|
-
orjson==3.
|
|
26
|
-
pydantic~=2.
|
|
25
|
+
orjson==3.10.3
|
|
26
|
+
pydantic~=2.7.1
|
|
27
27
|
aiohttp_retry==2.8.3
|
|
28
28
|
requests~=2.31.0
|
|
29
|
-
setuptools~=69.
|
|
29
|
+
setuptools~=69.5.1
|
|
30
30
|
jsonpath-ng==1.6.0
|
|
31
|
+
tzlocal==5.2
|
|
32
|
+
pytz==2021.3
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cryptography==
|
|
1
|
+
cryptography==42.0.6
|
|
2
2
|
jeepney==0.8.0
|
|
3
3
|
secretstorage==3.3.3
|
|
4
4
|
dbus-python==1.2.16
|
|
@@ -8,7 +8,7 @@ python-nubia==0.2b5
|
|
|
8
8
|
msal==1.26.0
|
|
9
9
|
tenacity==8.2.3
|
|
10
10
|
aiofiles==23.2.1
|
|
11
|
-
aiohttp[speedups]==3.9.
|
|
11
|
+
aiohttp[speedups]==3.9.5
|
|
12
12
|
aioretry==5.0.2
|
|
13
13
|
uvloop==0.19.0
|
|
14
14
|
arrow==1.3.0
|
|
@@ -18,9 +18,11 @@ PyJWT==2.8.0
|
|
|
18
18
|
django-environ==0.11.2
|
|
19
19
|
validators==0.22.0
|
|
20
20
|
pydot==2.0.0
|
|
21
|
-
orjson==3.
|
|
22
|
-
pydantic~=2.
|
|
21
|
+
orjson==3.10.3
|
|
22
|
+
pydantic~=2.7.1
|
|
23
23
|
aiohttp_retry==2.8.3
|
|
24
24
|
requests~=2.31.0
|
|
25
|
-
setuptools~=69.
|
|
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
|
|
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
|
{seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab/organization/resource/__init__.py
RENAMED
|
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
|
{seqslab-cli-3.3.2.post1 → seqslab-cli-3.3.4}/python/seqslab_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|