seqslab-cli 3.3.3__tar.gz → 3.3.5__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.5}/PKG-INFO +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/auth/azuread.py +2 -2
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/auth/commands.py +4 -2
- seqslab-cli-3.3.5/python/seqslab/color.py +8 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/api/base.py +3 -3
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/commands.py +115 -108
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/internal/aiocopy.py +11 -6
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/storage/azure.py +7 -5
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/utils/atgxmetadata.py +1 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/commands.py +15 -4
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/template/base.py +6 -31
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/commands.py +187 -79
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/internal/parameters.py +12 -29
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/resource/base.py +34 -2
- {seqslab-cli-3.3.3/python/seqslab/wes/internal → seqslab-cli-3.3.5/python/seqslab/wes/resource}/common.py +2 -2
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/template/base.py +13 -10
- seqslab-cli-3.3.5/python/seqslab/workspace/resource/azure.py +26 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/resource/base.py +13 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/SOURCES.txt +2 -1
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/requires.txt +8 -6
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/requirements.txt +8 -6
- seqslab-cli-3.3.3/python/seqslab/wes/resource/azure.py +0 -26
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/LICENSE +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/MANIFEST.in +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/README.md +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/cli.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/api/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/internal/utils.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/utils/biomimetype.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/organization/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/runsheet/runsheet.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/scr/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/settings.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/register/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/resource/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/template/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/user/resource/base.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3/python/seqslab/workspace → seqslab-cli-3.3.5/python/seqslab/wes}/resource/azure.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/template/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/commands.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/internal/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab/workspace/resource/__init__.py +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/setup.cfg +0 -0
- {seqslab-cli-3.3.3 → seqslab-cli-3.3.5}/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(
|
|
@@ -333,9 +333,9 @@ class DRSregister:
|
|
|
333
333
|
folder_stdin["access_methods"].clear()
|
|
334
334
|
for i, dsts in enumerate(dsts_list):
|
|
335
335
|
folder_stdin["access_methods"].append(access_method_infos[i])
|
|
336
|
-
folder_stdin["access_methods"][i]["access_url"][
|
|
337
|
-
"
|
|
338
|
-
|
|
336
|
+
folder_stdin["access_methods"][i]["access_url"]["url"] = (
|
|
337
|
+
self.common_root(dsts=dsts, workspace=kwargs.get("workspace"))
|
|
338
|
+
)
|
|
339
339
|
|
|
340
340
|
folder_stdin["name"] = kwargs.get(
|
|
341
341
|
"name",
|
|
@@ -15,10 +15,12 @@ from urllib.parse import unquote
|
|
|
15
15
|
|
|
16
16
|
from jsonpath_ng.ext import parse
|
|
17
17
|
from nubia import argument, command, context
|
|
18
|
+
from seqslab.color import color_handler
|
|
18
19
|
from seqslab.drs.utils.atgxmetadata import AtgxMetaData
|
|
19
20
|
from seqslab.drs.utils.progressbar import ProgressBarObject
|
|
20
21
|
from seqslab.exceptions import async_exception_handler, exception_handler
|
|
21
22
|
from seqslab.runsheet.runsheet import RunSheet
|
|
23
|
+
from seqslab.workspace.internal.common import get_factory as get_workspace_factory
|
|
22
24
|
from tabulate import tabulate
|
|
23
25
|
from termcolor import cprint
|
|
24
26
|
from yarl import URL
|
|
@@ -57,6 +59,7 @@ class BaseDatahub:
|
|
|
57
59
|
return context.get_context().args.proxy
|
|
58
60
|
|
|
59
61
|
@staticmethod
|
|
62
|
+
@color_handler
|
|
60
63
|
@exception_handler
|
|
61
64
|
def _upload(src: URL, dst: URL, recursive: bool, **kwargs) -> list:
|
|
62
65
|
# copy from local to cloud
|
|
@@ -67,33 +70,40 @@ class BaseDatahub:
|
|
|
67
70
|
if os.path.isdir(paths[0]):
|
|
68
71
|
if not recursive:
|
|
69
72
|
raise OSError("--recursive (-r) is Required.")
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
raise InterruptedError("Interrupt all the process.")
|
|
73
|
+
sys.stderr.write(
|
|
74
|
+
f"{kwargs['green']}"
|
|
75
|
+
+ "Destination Path: "
|
|
76
|
+
+ f"{kwargs['red']}"
|
|
77
|
+
+ f"{str(dst)}\n"
|
|
78
|
+
+ f"{kwargs['yellow']}\0"
|
|
79
|
+
)
|
|
78
80
|
coro = aiocopy.dir_to_blob(URL(*paths), dst, **kwargs)
|
|
79
81
|
else:
|
|
80
|
-
if
|
|
82
|
+
if str(dst).endswith("/"):
|
|
83
|
+
sys.stderr.write(
|
|
84
|
+
f"{kwargs['green']}"
|
|
85
|
+
+ "Destination Path: "
|
|
86
|
+
+ f"{kwargs['red']}"
|
|
87
|
+
+ f"{os.path.join(str(dst), os.path.basename(str(src)))} \n"
|
|
88
|
+
+ f"{kwargs['yellow']}\0"
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
81
91
|
sys.stderr.write(
|
|
82
|
-
f
|
|
83
|
-
|
|
92
|
+
f"{kwargs['green']}"
|
|
93
|
+
+ "Destination Path: "
|
|
94
|
+
+ f"{kwargs['red']}"
|
|
95
|
+
+ f"{str(dst)} \n"
|
|
96
|
+
+ f"{kwargs['yellow']}\0"
|
|
84
97
|
)
|
|
85
|
-
user_input = input()
|
|
86
|
-
if user_input.lower() != "y":
|
|
87
|
-
raise InterruptedError("Interrupt all the process.")
|
|
88
98
|
coro = aiocopy.file_to_blob([URL(*paths)], dst, **kwargs)
|
|
89
99
|
else:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
sys.stderr.write(
|
|
101
|
+
f"{kwargs['green']}"
|
|
102
|
+
+ "Destination Path: "
|
|
103
|
+
+ f"{kwargs['red']}"
|
|
104
|
+
+ f"cloud://{str(dst)}. \n"
|
|
105
|
+
+ f"{kwargs['yellow']}\0"
|
|
106
|
+
)
|
|
97
107
|
files = []
|
|
98
108
|
dirs = []
|
|
99
109
|
for p in paths:
|
|
@@ -156,6 +166,13 @@ class BaseDatahub:
|
|
|
156
166
|
"Stdin format only supports json. Please read the document and make sure the format is valid."
|
|
157
167
|
)
|
|
158
168
|
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _valide_workspace(workspace: str) -> bool:
|
|
171
|
+
ctx = context.get_context()
|
|
172
|
+
backend = ctx.args.backend
|
|
173
|
+
resource = get_workspace_factory().load_resource()
|
|
174
|
+
return resource.validate_workspace(workspace, backend)
|
|
175
|
+
|
|
159
176
|
@command(aliases=["copy"])
|
|
160
177
|
@argument(
|
|
161
178
|
"workspace",
|
|
@@ -173,7 +190,9 @@ class BaseDatahub:
|
|
|
173
190
|
"dst",
|
|
174
191
|
type=str,
|
|
175
192
|
positional=False,
|
|
176
|
-
description="Specify the destination directory, file path, or DRS URL
|
|
193
|
+
description="Specify the destination directory, file path, or DRS URL. Paths with leading slashes, leading "
|
|
194
|
+
"backslashes, or dot-only segments are invalid. Additionally, paths should not contain characters other than "
|
|
195
|
+
"[0-9a-zA-Z-._:/]. If not provided, the default is the current Linux epoch timestamp (optional).",
|
|
177
196
|
)
|
|
178
197
|
@argument(
|
|
179
198
|
"recursive",
|
|
@@ -194,15 +213,13 @@ class BaseDatahub:
|
|
|
194
213
|
"multiprocessing",
|
|
195
214
|
type=int,
|
|
196
215
|
positional=False,
|
|
197
|
-
description="Specify the number of files that you want to upload at any given time. "
|
|
198
|
-
"Changing the default value is not recommended (default = 1).",
|
|
216
|
+
description="Specify the number of files that you want to upload at any given time. (default = 1).",
|
|
199
217
|
)
|
|
200
218
|
@argument(
|
|
201
219
|
"chunk_size",
|
|
202
220
|
type=str,
|
|
203
221
|
positional=False,
|
|
204
|
-
description="Specify the size of each file that you want to upload in
|
|
205
|
-
"Changing the default value is not recommended (default = 8MB).",
|
|
222
|
+
description="Specify the size of each file that you want to upload in mebibyte (MiB). (default = 8MiB).",
|
|
206
223
|
choices=["8", "16"],
|
|
207
224
|
)
|
|
208
225
|
@argument(
|
|
@@ -212,28 +229,19 @@ class BaseDatahub:
|
|
|
212
229
|
description="Specify the stdout format (default = json).",
|
|
213
230
|
choices=["json", "table", "tsv"],
|
|
214
231
|
)
|
|
215
|
-
@argument(
|
|
216
|
-
"non_interactive",
|
|
217
|
-
type=bool,
|
|
218
|
-
positional=False,
|
|
219
|
-
description="Whether to run in non-interactive mode, i.e. without prompt (default = False).",
|
|
220
|
-
aliases=["ni"],
|
|
221
|
-
)
|
|
222
232
|
def upload(
|
|
223
233
|
self,
|
|
224
234
|
workspace: str,
|
|
225
235
|
src: str,
|
|
226
|
-
dst: str = "
|
|
236
|
+
dst: str = f"{str(int(datetime.datetime.now().timestamp()))}/",
|
|
227
237
|
recursive: bool = False,
|
|
228
238
|
output: str = "json",
|
|
229
239
|
concurrency: int = 0,
|
|
230
240
|
multiprocessing: int = 1,
|
|
231
241
|
chunk_size: str = "8",
|
|
232
|
-
expiry_time: str = None,
|
|
233
|
-
non_interactive: bool = False,
|
|
234
242
|
) -> int:
|
|
235
243
|
"""
|
|
236
|
-
Upload data from the local file system to a SeqsLab Data Hub cloud service.
|
|
244
|
+
Upload data from the local file system to a SeqsLab Data Hub cloud service. This command behaves similarly to gsutil cp command.
|
|
237
245
|
"""
|
|
238
246
|
|
|
239
247
|
def __log(results: List[dict], output: str):
|
|
@@ -249,12 +257,26 @@ class BaseDatahub:
|
|
|
249
257
|
else:
|
|
250
258
|
logging.info(msg)
|
|
251
259
|
|
|
252
|
-
if not workspace:
|
|
253
|
-
logging.error("
|
|
254
|
-
cprint("
|
|
260
|
+
if not self._valide_workspace(workspace):
|
|
261
|
+
logging.error("Workspace not found")
|
|
262
|
+
cprint(f"Workspace {workspace} not found.", "red")
|
|
255
263
|
return errno.EINVAL
|
|
256
264
|
|
|
257
|
-
if dst
|
|
265
|
+
if dst:
|
|
266
|
+
# Standard Library
|
|
267
|
+
import re
|
|
268
|
+
|
|
269
|
+
invalid_patterns = [
|
|
270
|
+
r"(^|/|\\)\.{1,}($|/|\\|$)|^\s*$|^\/$|^\.$|^/|//", # ex: ., .., ..., ./, ../, .../, ././. etc
|
|
271
|
+
r"[^0-9a-zA-Z\-\._:/]+",
|
|
272
|
+
# ex: Only alphanumeric characters, hyphen, period, colon, and underscore are allowed.
|
|
273
|
+
]
|
|
274
|
+
for pattern in invalid_patterns:
|
|
275
|
+
if re.search(pattern, dst):
|
|
276
|
+
logging.error(f"Invalid dst path {dst}")
|
|
277
|
+
cprint(f"Invalid dst path {dst}.", "red")
|
|
278
|
+
return errno.EINVAL
|
|
279
|
+
else:
|
|
258
280
|
logging.error("Invalid dst path")
|
|
259
281
|
cprint("Enter a valid dst path.", "red")
|
|
260
282
|
return errno.EINVAL
|
|
@@ -262,19 +284,7 @@ class BaseDatahub:
|
|
|
262
284
|
if os.path.isdir(src):
|
|
263
285
|
if src.endswith("/"):
|
|
264
286
|
src = URL(f'{str(src).rstrip("/")}', encoded=True)
|
|
265
|
-
if expiry_time:
|
|
266
|
-
# Standard Library
|
|
267
|
-
import re
|
|
268
|
-
|
|
269
|
-
pattern = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}"
|
|
270
|
-
if not re.fullmatch(pattern, expiry_time):
|
|
271
|
-
cprint(
|
|
272
|
-
"Make sure expiry_time format is correct. e.g. 2023-12-31 00:00:00",
|
|
273
|
-
"red",
|
|
274
|
-
)
|
|
275
|
-
return errno.EINVAL
|
|
276
287
|
|
|
277
|
-
dst = "/" if dst == "." else dst
|
|
278
288
|
result = self._upload(
|
|
279
289
|
src=URL(src, encoded=True),
|
|
280
290
|
dst=URL(dst, encoded=True),
|
|
@@ -284,8 +294,6 @@ class BaseDatahub:
|
|
|
284
294
|
concurrency=concurrency,
|
|
285
295
|
chunk_size=self.size[chunk_size],
|
|
286
296
|
proxy=self.proxy,
|
|
287
|
-
non_interactive=non_interactive,
|
|
288
|
-
expiry_time=expiry_time,
|
|
289
297
|
)
|
|
290
298
|
if isinstance(result, int):
|
|
291
299
|
return result
|
|
@@ -322,23 +330,20 @@ class BaseDatahub:
|
|
|
322
330
|
type=int,
|
|
323
331
|
positional=False,
|
|
324
332
|
description="Specify the numbers of chunks that can be transferred concurrently. The download bandwidth "
|
|
325
|
-
"can be estimated using the formula concurrency * chunk_size. "
|
|
326
|
-
"Changing the default value is not recommended (optional, default = 120).",
|
|
333
|
+
"can be estimated using the formula concurrency * chunk_size. (optional, default = 120).",
|
|
327
334
|
)
|
|
328
335
|
@argument(
|
|
329
336
|
"multiprocessing",
|
|
330
337
|
type=int,
|
|
331
338
|
positional=False,
|
|
332
|
-
description="Specify the number of files that you want to upload at any given time. "
|
|
333
|
-
"Changing the default value is not recommended (default = 1).",
|
|
339
|
+
description="Specify the number of files that you want to upload at any given time. (default = 1).",
|
|
334
340
|
)
|
|
335
341
|
@argument(
|
|
336
342
|
"chunk_size",
|
|
337
343
|
type=str,
|
|
338
344
|
positional=False,
|
|
339
345
|
description="Specify the size of chunk in the chunked downloading process. "
|
|
340
|
-
"If the specified size is greater than
|
|
341
|
-
"Changing the default value is not recommended (optional, default = 4MB).",
|
|
346
|
+
"If the specified size is greater than 4MiB, the md5 hashes are not checked. (optional, default = 4MiB).",
|
|
342
347
|
choices=["4", "8", "16", "32"],
|
|
343
348
|
)
|
|
344
349
|
@argument(
|
|
@@ -384,9 +389,9 @@ class BaseDatahub:
|
|
|
384
389
|
else:
|
|
385
390
|
logging.info(msg)
|
|
386
391
|
|
|
387
|
-
if not workspace:
|
|
388
|
-
logging.error("
|
|
389
|
-
cprint("
|
|
392
|
+
if not self._valide_workspace(workspace):
|
|
393
|
+
logging.error("Workspace not found")
|
|
394
|
+
cprint(f"Workspace {workspace} not found.", "red")
|
|
390
395
|
return errno.EINVAL
|
|
391
396
|
|
|
392
397
|
tasks = []
|
|
@@ -461,9 +466,9 @@ class BaseDatahub:
|
|
|
461
466
|
browsers. The share link expires either after the first successful download or after a period of three months.
|
|
462
467
|
"""
|
|
463
468
|
|
|
464
|
-
if not workspace:
|
|
465
|
-
logging.error("
|
|
466
|
-
cprint("
|
|
469
|
+
if not self._valide_workspace(workspace):
|
|
470
|
+
logging.error("Workspace not found")
|
|
471
|
+
cprint("Workspace not found.", "red")
|
|
467
472
|
return errno.EINVAL
|
|
468
473
|
|
|
469
474
|
api_backend = drs_register().load_register(workspace)
|
|
@@ -663,6 +668,10 @@ class BaseDatahub:
|
|
|
663
668
|
"""
|
|
664
669
|
Register a DRS object as a blob.
|
|
665
670
|
"""
|
|
671
|
+
if not self._valide_workspace(workspace):
|
|
672
|
+
logging.error("Workspace not found")
|
|
673
|
+
cprint("Workspace not found.", "red")
|
|
674
|
+
return errno.EINVAL
|
|
666
675
|
|
|
667
676
|
def __log(results: List[dict], output: str):
|
|
668
677
|
self._stdout(results=results, output=output)
|
|
@@ -725,17 +734,21 @@ class BaseDatahub:
|
|
|
725
734
|
"mime_type": kwargs.get("mime_type"),
|
|
726
735
|
"file_type": kwargs.get("file_type"),
|
|
727
736
|
"created_time": kwargs.get("created_time"),
|
|
728
|
-
"updated_time":
|
|
729
|
-
|
|
730
|
-
|
|
737
|
+
"updated_time": (
|
|
738
|
+
kwargs.get("updated_time")
|
|
739
|
+
if kwargs.get("updated_time")
|
|
740
|
+
else kwargs.get("created_time")
|
|
741
|
+
),
|
|
731
742
|
"size": kwargs.get("size"),
|
|
732
743
|
"access_methods": json.loads(kwargs.get("access_methods")),
|
|
733
744
|
"checksums": checksums,
|
|
734
745
|
"description": kwargs.get("description"),
|
|
735
746
|
"aliases": kwargs.get("aliases"),
|
|
736
|
-
"metadata":
|
|
737
|
-
|
|
738
|
-
|
|
747
|
+
"metadata": (
|
|
748
|
+
json.loads(kwargs.get("metadata"))
|
|
749
|
+
if kwargs.get("metadata")
|
|
750
|
+
else None
|
|
751
|
+
),
|
|
739
752
|
"tags": kwargs.get("tags"),
|
|
740
753
|
"id": kwargs.get("id"),
|
|
741
754
|
"deleted_time": kwargs.get("deleted_time"),
|
|
@@ -828,8 +841,9 @@ class BaseDatahub:
|
|
|
828
841
|
msg = f"{r['id']} has been registered."
|
|
829
842
|
logging.info(msg)
|
|
830
843
|
|
|
831
|
-
if not workspace:
|
|
832
|
-
|
|
844
|
+
if not self._valide_workspace(workspace):
|
|
845
|
+
logging.error("Workspace not found")
|
|
846
|
+
cprint("Workspace not found.", "red")
|
|
833
847
|
return errno.EINVAL
|
|
834
848
|
|
|
835
849
|
results = self._register_bundle(
|
|
@@ -856,9 +870,11 @@ class BaseDatahub:
|
|
|
856
870
|
"file_type": "bundle",
|
|
857
871
|
"description": kwargs.get("description"),
|
|
858
872
|
"aliases": kwargs.get("aliases"),
|
|
859
|
-
"metadata":
|
|
860
|
-
|
|
861
|
-
|
|
873
|
+
"metadata": (
|
|
874
|
+
json.loads(kwargs.get("metadata"))
|
|
875
|
+
if kwargs.get("metadata")
|
|
876
|
+
else None
|
|
877
|
+
),
|
|
862
878
|
"tags": kwargs.get("tags"),
|
|
863
879
|
"id": kwargs.get("id"),
|
|
864
880
|
"contents": drs_ids,
|
|
@@ -872,7 +888,7 @@ class BaseDatahub:
|
|
|
872
888
|
|
|
873
889
|
@command(aliases=["deregister"])
|
|
874
890
|
@argument(
|
|
875
|
-
"
|
|
891
|
+
"id",
|
|
876
892
|
type=List[str],
|
|
877
893
|
positional=False,
|
|
878
894
|
description="Specify the IDs of the DRS objects that you want to delete "
|
|
@@ -894,14 +910,14 @@ class BaseDatahub:
|
|
|
894
910
|
)
|
|
895
911
|
def deregister(
|
|
896
912
|
self,
|
|
897
|
-
|
|
913
|
+
id: List[str] = [],
|
|
898
914
|
tags: List[str] = [],
|
|
899
915
|
names: List[str] = [],
|
|
900
916
|
) -> int:
|
|
901
917
|
"""
|
|
902
918
|
Deregister DRS objects by DRS ID, DRS name, or DRS tag.
|
|
903
919
|
"""
|
|
904
|
-
if not
|
|
920
|
+
if not id and not names and not tags:
|
|
905
921
|
cprint(
|
|
906
922
|
"Must specify one of the IDs, names or tags to identify DRS objects for deletion",
|
|
907
923
|
"red",
|
|
@@ -909,7 +925,7 @@ class BaseDatahub:
|
|
|
909
925
|
return errno.ENOENT
|
|
910
926
|
|
|
911
927
|
resps = asyncio.run(
|
|
912
|
-
utils.drs_delete(
|
|
928
|
+
utils.drs_delete(id, names, tags, query_opts="?backend_content=false")
|
|
913
929
|
)
|
|
914
930
|
for r in resps:
|
|
915
931
|
cprint(r, "yellow")
|
|
@@ -918,7 +934,7 @@ class BaseDatahub:
|
|
|
918
934
|
|
|
919
935
|
@command(aliases=["clean"])
|
|
920
936
|
@argument(
|
|
921
|
-
"
|
|
937
|
+
"id",
|
|
922
938
|
type=List[str],
|
|
923
939
|
positional=False,
|
|
924
940
|
description="Specify the IDs of the DRS objects that you want to delete "
|
|
@@ -940,14 +956,14 @@ class BaseDatahub:
|
|
|
940
956
|
)
|
|
941
957
|
def delete(
|
|
942
958
|
self,
|
|
943
|
-
|
|
959
|
+
id: List[str] = [],
|
|
944
960
|
tags: List[str] = [],
|
|
945
961
|
names: List[str] = [],
|
|
946
962
|
) -> int:
|
|
947
963
|
"""
|
|
948
964
|
Delete DRS objects and the associated files in cloud storage by DRS ID, DRS name, or DRS tag.
|
|
949
965
|
"""
|
|
950
|
-
if not
|
|
966
|
+
if not id and not names and not tags:
|
|
951
967
|
cprint(
|
|
952
968
|
"Must specify one of the IDs, names or tags to identify "
|
|
953
969
|
"the associated files in cloud storage of DRS objects for deletion",
|
|
@@ -956,7 +972,7 @@ class BaseDatahub:
|
|
|
956
972
|
return errno.ENOENT
|
|
957
973
|
|
|
958
974
|
resps = asyncio.run(
|
|
959
|
-
utils.drs_delete(
|
|
975
|
+
utils.drs_delete(id, names, tags, query_opts="?backend_content=true")
|
|
960
976
|
)
|
|
961
977
|
for r in resps:
|
|
962
978
|
cprint(r, "yellow")
|
|
@@ -1202,24 +1218,15 @@ class BaseDatahub:
|
|
|
1202
1218
|
"multiprocessing",
|
|
1203
1219
|
type=int,
|
|
1204
1220
|
positional=False,
|
|
1205
|
-
description="Specify the number of files that you want to upload at any given time. "
|
|
1206
|
-
"Changing the default value is not recommended (optional, default = 1).",
|
|
1221
|
+
description="Specify the number of files that you want to upload at any given time. (optional, default = 1).",
|
|
1207
1222
|
)
|
|
1208
1223
|
@argument(
|
|
1209
1224
|
"chunk_size",
|
|
1210
1225
|
type=str,
|
|
1211
1226
|
positional=False,
|
|
1212
|
-
description="Specify the size of each file that you want to upload in
|
|
1213
|
-
"Changing the default value is not recommended (default = 8MB).",
|
|
1227
|
+
description="Specify the size of each file that you want to upload in mebibyte (MiB). (default = 8MiB).",
|
|
1214
1228
|
choices=["8", "16"],
|
|
1215
1229
|
)
|
|
1216
|
-
@argument(
|
|
1217
|
-
"non_interactive",
|
|
1218
|
-
type=bool,
|
|
1219
|
-
positional=False,
|
|
1220
|
-
description="Whether to run in non-interactive mode, i.e. without prompt (default = False).",
|
|
1221
|
-
aliases=["ni"],
|
|
1222
|
-
)
|
|
1223
1230
|
@argument(
|
|
1224
1231
|
"seq_run_id",
|
|
1225
1232
|
type=str,
|
|
@@ -1237,17 +1244,16 @@ class BaseDatahub:
|
|
|
1237
1244
|
concurrency: int = 0,
|
|
1238
1245
|
multiprocessing: int = 1,
|
|
1239
1246
|
chunk_size: str = "8",
|
|
1240
|
-
expiry_time: str = None,
|
|
1241
|
-
non_interactive: bool = False,
|
|
1242
1247
|
seq_run_id: str = "",
|
|
1243
1248
|
) -> int:
|
|
1244
1249
|
"""
|
|
1245
1250
|
Upload samples based on the Run Sheet and FASTQ path information.
|
|
1246
1251
|
"""
|
|
1247
|
-
if not workspace:
|
|
1248
|
-
logging.error("
|
|
1249
|
-
cprint("
|
|
1252
|
+
if not self._valide_workspace(workspace):
|
|
1253
|
+
logging.error("Workspace not found")
|
|
1254
|
+
cprint("Workspace not found.", "red")
|
|
1250
1255
|
return errno.EINVAL
|
|
1256
|
+
|
|
1251
1257
|
failed = False
|
|
1252
1258
|
if not upload_dst:
|
|
1253
1259
|
upload_dst = int(datetime.datetime.timestamp(datetime.datetime.now()))
|
|
@@ -1282,8 +1288,7 @@ class BaseDatahub:
|
|
|
1282
1288
|
concurrency=concurrency,
|
|
1283
1289
|
chunk_size=self.size[chunk_size],
|
|
1284
1290
|
proxy=self.proxy,
|
|
1285
|
-
non_interactive=
|
|
1286
|
-
expiry_time=expiry_time,
|
|
1291
|
+
non_interactive=False,
|
|
1287
1292
|
)
|
|
1288
1293
|
if isinstance(result, list):
|
|
1289
1294
|
r = result[0]
|
|
@@ -1375,8 +1380,9 @@ class BaseDatahub:
|
|
|
1375
1380
|
"""
|
|
1376
1381
|
Update a DRS object.
|
|
1377
1382
|
"""
|
|
1378
|
-
if not workspace:
|
|
1379
|
-
|
|
1383
|
+
if not self._valide_workspace(workspace):
|
|
1384
|
+
logging.error("Workspace not found")
|
|
1385
|
+
cprint("Workspace not found.", "red")
|
|
1380
1386
|
return errno.EINVAL
|
|
1381
1387
|
|
|
1382
1388
|
if stdin:
|
|
@@ -1427,8 +1433,9 @@ class BaseDatahub:
|
|
|
1427
1433
|
"""
|
|
1428
1434
|
Get a DRS object.
|
|
1429
1435
|
"""
|
|
1430
|
-
if not workspace:
|
|
1431
|
-
|
|
1436
|
+
if not self._valide_workspace(workspace):
|
|
1437
|
+
logging.error("Workspace not found")
|
|
1438
|
+
cprint("Workspace not found.", "red")
|
|
1432
1439
|
return errno.EINVAL
|
|
1433
1440
|
|
|
1434
1441
|
def __log(results: dict):
|
|
@@ -193,9 +193,11 @@ async def result_setting(status: list, files: List[URL], resp_list: list) -> Lis
|
|
|
193
193
|
name = os.path.basename(resp["dst"][0]).replace(f".{file_extension}", "")
|
|
194
194
|
access_methods = [
|
|
195
195
|
CopyResult.access_method(
|
|
196
|
-
access_methods_type=
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
access_methods_type=(
|
|
197
|
+
resp["access_methods_type"][i]
|
|
198
|
+
if resp.get("access_methods_type")
|
|
199
|
+
else None
|
|
200
|
+
),
|
|
199
201
|
access_tier="hot",
|
|
200
202
|
dst=dst,
|
|
201
203
|
region=resp["region"],
|
|
@@ -208,7 +210,7 @@ async def result_setting(status: list, files: List[URL], resp_list: list) -> Lis
|
|
|
208
210
|
file_type=file_extension,
|
|
209
211
|
created_time=resp["created_time"],
|
|
210
212
|
size=sent,
|
|
211
|
-
aliases=[
|
|
213
|
+
aliases=[],
|
|
212
214
|
access_methods=access_methods if status == "complete" else None,
|
|
213
215
|
checksums=checksums if status == "complete" else None,
|
|
214
216
|
status=status,
|
|
@@ -381,14 +383,17 @@ async def dir_to_blob(dir: URL, dst: URL, **kwargs) -> List[dict]:
|
|
|
381
383
|
"""
|
|
382
384
|
files = []
|
|
383
385
|
relpath = []
|
|
384
|
-
root_path = os.path.basename(dir.human_repr())
|
|
385
386
|
for root, dirlist, filelist in os.walk(dir.human_repr()):
|
|
386
387
|
if filelist:
|
|
387
388
|
for file in filelist:
|
|
389
|
+
if file.startswith("."):
|
|
390
|
+
# skip file start with '.'
|
|
391
|
+
print(f"skip {file}")
|
|
392
|
+
continue
|
|
388
393
|
absolute_path = os.path.join(root, file)
|
|
389
394
|
relative_path = os.path.relpath(absolute_path, dir.human_repr())
|
|
390
395
|
files.append(URL(absolute_path))
|
|
391
|
-
relpath.append(
|
|
396
|
+
relpath.append(relative_path)
|
|
392
397
|
|
|
393
398
|
async with get_factory().load_storage(kwargs.get("workspace")) as store:
|
|
394
399
|
progress: int = 0
|
|
@@ -517,11 +517,13 @@ class BlobStorage(BaseBlob):
|
|
|
517
517
|
),
|
|
518
518
|
"region": workspace["location"],
|
|
519
519
|
"access_methods_type": workspace["resources"][0]["type"],
|
|
520
|
-
"exception":
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
520
|
+
"exception": (
|
|
521
|
+
ValueError(
|
|
522
|
+
"Make sure the the file needs to be overwritten or rename the dst name."
|
|
523
|
+
)
|
|
524
|
+
if exist_size != file_size
|
|
525
|
+
else None
|
|
526
|
+
),
|
|
525
527
|
}
|
|
526
528
|
elif os.path.exists(temp_file):
|
|
527
529
|
# start with temp
|
|
@@ -10,6 +10,7 @@ preparation of derivative works based upon the program or distribution of
|
|
|
10
10
|
copies by sale, rental, lease or lending are violations of federal copyright
|
|
11
11
|
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
12
12
|
"""
|
|
13
|
+
|
|
13
14
|
# Standard Library
|
|
14
15
|
import re
|
|
15
16
|
|