materials-commons-cli 2.0b9__tar.gz → 2.1.1__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.
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/PKG-INFO +9 -8
- materials_commons-cli-2.1.1/materials_commons/cli/__init__.py +1 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/functions.py +14 -14
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/globus.py +61 -11
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/parser.py +4 -47
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/dataset.py +8 -3
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/down.py +3 -4
- materials_commons-cli-2.1.1/materials_commons/cli/subcommands/fetch.py +92 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/globus.py +7 -2
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/ls.py +4 -1
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/up.py +12 -4
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/tree_functions.py +553 -37
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/treedb.py +48 -19
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/user_config.py +0 -23
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/PKG-INFO +9 -8
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/SOURCES.txt +1 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/entry_points.txt +0 -1
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/requires.txt +1 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/setup.py +1 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_user_config.py +0 -1
- materials_commons-cli-2.0b9/materials_commons/cli/__init__.py +0 -1
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/LICENSE +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/README.md +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/LICENSE.txt +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/cloned_project.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/exceptions.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/file_functions.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/list_objects.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/print_formatter.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/sqltable.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/__init__.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/clone.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/config.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/expt.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/init.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/mkdir.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/mv.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/proj.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/remote.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/rm.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/versions.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/tmp_functions.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/dependency_links.txt +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/not-zip-safe +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons_cli.egg-info/top_level.txt +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/setup.cfg +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_api.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_functions.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_dataset.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_down.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_mkdir.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_mv.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_proj.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_rm.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_mc_up.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_treecompare_no_cache.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_treecompare_with_cache.py +0 -0
- {materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/test/test_treedb.py +0 -0
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
Metadata-Version:
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: materials_commons-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Materials Commons CLI
|
|
5
5
|
Home-page: https://materials-commons.github.io/materials-commons-cli/html/index.html
|
|
6
6
|
Author: Materials Commons development team
|
|
7
7
|
Author-email: materials-commons-authors@umich.edu
|
|
8
8
|
License: MIT
|
|
9
|
-
Description: This package contains the materials_commons.cli module. This module is an interface
|
|
10
|
-
to the Materials Commons project. We assume you have used (or are otherwise familiar with) the Materials
|
|
11
|
-
Commons web site, https://materialscommons.org/, or a similar site based on the
|
|
12
|
-
Materials Commons code (https://github.com/materials-commons/materialscommons), and intend to use these
|
|
13
|
-
tools in that context.
|
|
14
9
|
Keywords: materials science mc materials-commons prisms
|
|
15
|
-
Platform: UNKNOWN
|
|
16
10
|
Classifier: Development Status :: 4 - Beta
|
|
17
11
|
Classifier: Intended Audience :: Developers
|
|
18
12
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -20,3 +14,10 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
|
20
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
21
15
|
Classifier: Programming Language :: Python :: 3
|
|
22
16
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
|
|
19
|
+
This package contains the materials_commons.cli module. This module is an interface
|
|
20
|
+
to the Materials Commons project. We assume you have used (or are otherwise familiar with) the Materials
|
|
21
|
+
Commons web site, https://materialscommons.org/, or a similar site based on the
|
|
22
|
+
Materials Commons code (https://github.com/materials-commons/materialscommons), and intend to use these
|
|
23
|
+
tools in that context.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.1.1'
|
{materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/functions.py
RENAMED
|
@@ -60,21 +60,21 @@ def epoch_time(time_value):
|
|
|
60
60
|
"""Attempts to convert various time representations into s since the epoch
|
|
61
61
|
|
|
62
62
|
Args:
|
|
63
|
-
time_value: A representation of time.
|
|
63
|
+
time_value: A representation of time, expects UTC time.
|
|
64
64
|
|
|
65
65
|
Returns:
|
|
66
|
-
An
|
|
66
|
+
An float s since the epoch. Uses the following:
|
|
67
67
|
|
|
68
68
|
+-------------------+------------------------------------------------------------+
|
|
69
69
|
| If this type | Then do this conversion |
|
|
70
70
|
+-------------------+------------------------------------------------------------+
|
|
71
|
-
| str |
|
|
71
|
+
| str | dateutil.parser.parse(time_value).timestamp() |
|
|
72
72
|
+-------------------+------------------------------------------------------------+
|
|
73
73
|
| float, int | time_value |
|
|
74
74
|
+-------------------+------------------------------------------------------------+
|
|
75
|
-
| datetime.datetime |
|
|
75
|
+
| datetime.datetime | time_value.replace(tzinfo=datetime.timezone.utc).timestamp() |
|
|
76
76
|
+-------------------+------------------------------------------------------------+
|
|
77
|
-
| dict | time_value['epoch_time']
|
|
77
|
+
| dict | float(time_value['epoch_time']) |
|
|
78
78
|
+-------------------+------------------------------------------------------------+
|
|
79
79
|
| None | None |
|
|
80
80
|
+-------------------+------------------------------------------------------------+
|
|
@@ -82,13 +82,13 @@ def epoch_time(time_value):
|
|
|
82
82
|
+-------------------+------------------------------------------------------------+
|
|
83
83
|
"""
|
|
84
84
|
if isinstance(time_value, str): # expect ISO 8601 str
|
|
85
|
-
return
|
|
85
|
+
return dateutil.parser.parse(time_value).timestamp()
|
|
86
86
|
elif isinstance(time_value, (float, int)):
|
|
87
|
-
return time_value
|
|
87
|
+
return float(time_value)
|
|
88
88
|
elif isinstance(time_value, datetime.datetime):
|
|
89
|
-
return
|
|
89
|
+
return time_value.replace(tzinfo=datetime.timezone.utc).timestamp()
|
|
90
90
|
elif isinstance(time_value, dict) and ('epoch_time' in time_value):
|
|
91
|
-
return time_value['epoch_time']
|
|
91
|
+
return float(time_value['epoch_time'])
|
|
92
92
|
elif time_value is None:
|
|
93
93
|
return None
|
|
94
94
|
else:
|
|
@@ -98,12 +98,12 @@ def format_time(time_value, fmt="%Y %b %d %H:%M:%S"):
|
|
|
98
98
|
"""Attempts to put various time representations into specified format for printing
|
|
99
99
|
|
|
100
100
|
Args:
|
|
101
|
-
time_value: A representation of time.
|
|
101
|
+
time_value: A representation of time, expects UTC time.
|
|
102
102
|
|
|
103
103
|
fmt (str): Format to use for the return value.
|
|
104
104
|
|
|
105
105
|
Returns:
|
|
106
|
-
A string representation using the specified format. Uses the following:
|
|
106
|
+
A string representation, in local time, using the specified format. Uses the following:
|
|
107
107
|
|
|
108
108
|
+-------------------+-------------------------------------------------+
|
|
109
109
|
| If this type | Then do this conversion |
|
|
@@ -112,7 +112,7 @@ def format_time(time_value, fmt="%Y %b %d %H:%M:%S"):
|
|
|
112
112
|
+-------------------+-------------------------------------------------+
|
|
113
113
|
| float, int | time.strftime(fmt, time.localtime(time_value)) |
|
|
114
114
|
+-------------------+-------------------------------------------------+
|
|
115
|
-
| datetime.datetime |
|
|
115
|
+
| datetime.datetime | time_value.strftime(fmt) |
|
|
116
116
|
+-------------------+-------------------------------------------------+
|
|
117
117
|
| dict | format_time(time_value['epoch_time']) |
|
|
118
118
|
+-------------------+-------------------------------------------------+
|
|
@@ -122,11 +122,11 @@ def format_time(time_value, fmt="%Y %b %d %H:%M:%S"):
|
|
|
122
122
|
+-------------------+-------------------------------------------------+
|
|
123
123
|
"""
|
|
124
124
|
if isinstance(time_value, str): # expect ISO 8601 str
|
|
125
|
-
return dateutil.parser.parse(time_value).strftime(fmt)
|
|
125
|
+
return dateutil.parser.parse(time_value).astimezone().strftime(fmt)
|
|
126
126
|
elif isinstance(time_value, (float, int)):
|
|
127
127
|
return time.strftime(fmt, time.localtime(time_value))
|
|
128
128
|
elif isinstance(time_value, datetime.datetime):
|
|
129
|
-
return time_value.strftime(fmt)
|
|
129
|
+
return time_value.astimezone().strftime(fmt)
|
|
130
130
|
elif isinstance(time_value, dict) and ('epoch_time' in time_value):
|
|
131
131
|
return format_time(time_value['epoch_time'])
|
|
132
132
|
elif time_value is None:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import globus_sdk
|
|
2
|
+
from globus_sdk.scopes import TransferScopes
|
|
2
3
|
import os
|
|
3
4
|
|
|
4
5
|
import materials_commons.api as mcapi
|
|
@@ -9,8 +10,16 @@ from materials_commons.cli.user_config import Config
|
|
|
9
10
|
|
|
10
11
|
CLIENT_ID = '1e4aacbc-8c10-4812-a54a-8434d2030a41'
|
|
11
12
|
|
|
13
|
+
def check_for_consent_required(client, target):
|
|
14
|
+
try:
|
|
15
|
+
client.operation_ls(target, path="/")
|
|
16
|
+
except globus_sdk.TransferAPIError as err:
|
|
17
|
+
if err.info.consent_required:
|
|
18
|
+
return err.info.consent_required.required_scopes
|
|
19
|
+
return None
|
|
20
|
+
|
|
12
21
|
|
|
13
|
-
def get_transfer_rt_or_login():
|
|
22
|
+
def get_transfer_rt_or_login(scopes=TransferScopes.all, reauth=False):
|
|
14
23
|
"""Get the Globus transfer refresh token, prompting for login if necessary
|
|
15
24
|
|
|
16
25
|
If not yet configured, will prompt user to login and enter a code used to obtain the refresh
|
|
@@ -25,13 +34,14 @@ def get_transfer_rt_or_login():
|
|
|
25
34
|
config = Config()
|
|
26
35
|
|
|
27
36
|
# return existing transfer_rt
|
|
28
|
-
if
|
|
29
|
-
|
|
37
|
+
if not reauth:
|
|
38
|
+
if config.globus.transfer_rt:
|
|
39
|
+
return config.globus.transfer_rt
|
|
30
40
|
|
|
31
41
|
# authenticate
|
|
32
42
|
|
|
33
43
|
client = globus_sdk.NativeAppAuthClient(CLIENT_ID)
|
|
34
|
-
client.oauth2_start_flow(refresh_tokens=True)
|
|
44
|
+
client.oauth2_start_flow(refresh_tokens=True, requested_scopes=scopes)
|
|
35
45
|
|
|
36
46
|
authorize_url = client.oauth2_get_authorize_url()
|
|
37
47
|
try:
|
|
@@ -202,31 +212,50 @@ class GlobusOperations(object):
|
|
|
202
212
|
return
|
|
203
213
|
|
|
204
214
|
|
|
205
|
-
def upload_v0(self, proj, paths, upload, working_dir, recursive=False,
|
|
215
|
+
def upload_v0(self, proj, paths, upload, working_dir, recursive=False, no_compare=False,
|
|
206
216
|
label=None, localtree=None, remotetree=None):
|
|
207
217
|
"""Upload files and directories to project
|
|
208
218
|
|
|
209
219
|
Arguments:
|
|
210
220
|
proj: Project
|
|
211
|
-
paths
|
|
221
|
+
paths (List of str): Materials Commons paths (include project directory) to upload
|
|
212
222
|
upload: mcapi.GlobusUpload, Globus upload request
|
|
213
223
|
working_dir (str): Current working directory, used for finding
|
|
214
224
|
relative paths and printing messages.
|
|
215
225
|
recursive: bool, If True, upload directories recursively
|
|
226
|
+
no_compare (bool): If False (default), compare checksum to skip overwriting equivalent
|
|
227
|
+
files. If True, transfer without checking.
|
|
216
228
|
label: str, Globus transfer label to make finding tasks simpler
|
|
217
229
|
|
|
218
230
|
Returns:
|
|
219
231
|
None or task_id: str, transfer task id. Returns nothing to transfer.
|
|
220
232
|
"""
|
|
221
|
-
paths = treefuncs.make_mcpaths_for_upload(proj.local_path, paths)
|
|
222
|
-
|
|
223
233
|
if not len(paths):
|
|
224
234
|
return None
|
|
225
235
|
|
|
226
236
|
files_data, dirs_data, child_data, non_existing = treefuncs.treecompare(proj, paths, checksum=True, localtree=localtree, remotetree=remotetree)
|
|
227
237
|
|
|
228
238
|
# https://globus-sdk-python.readthedocs.io/en/stable/clients/transfer/#globus_sdk.TransferData
|
|
229
|
-
|
|
239
|
+
sync_level = "checksum"
|
|
240
|
+
if no_compare is True:
|
|
241
|
+
sync_level = None
|
|
242
|
+
|
|
243
|
+
consent_required_scopes = []
|
|
244
|
+
consent_required = check_for_consent_required(self.tc, self.local_endpoint_id)
|
|
245
|
+
if consent_required is not None:
|
|
246
|
+
consent_required_scopes.extend(consent_required)
|
|
247
|
+
|
|
248
|
+
consent_required = check_for_consent_required(self.tc, upload.globus_endpoint_id)
|
|
249
|
+
if consent_required is not None:
|
|
250
|
+
consent_required_scopes.extend(consent_required)
|
|
251
|
+
|
|
252
|
+
if consent_required_scopes:
|
|
253
|
+
print("One or more of the endpoints requires consent in order to be used.\n"
|
|
254
|
+
"You must login a second time to grant consents.\n\n")
|
|
255
|
+
tr = get_transfer_rt_or_login(scopes=consent_required_scopes, reauth=True)
|
|
256
|
+
self.tc = make_transfer_client(tr)
|
|
257
|
+
|
|
258
|
+
tdata = globus_sdk.TransferData(self.tc, self.local_endpoint_id, upload.globus_endpoint_id, label=label, sync_level=sync_level)
|
|
230
259
|
|
|
231
260
|
# add items
|
|
232
261
|
n_items = 0
|
|
@@ -270,7 +299,7 @@ class GlobusOperations(object):
|
|
|
270
299
|
|
|
271
300
|
return task_id
|
|
272
301
|
|
|
273
|
-
def download_v0(self, proj, paths, download, working_dir, recursive=False,
|
|
302
|
+
def download_v0(self, proj, paths, download, working_dir, recursive=False, no_compare=False,
|
|
274
303
|
label=None, localtree=None, remotetree=None, force=False):
|
|
275
304
|
"""Download files and directories from project
|
|
276
305
|
|
|
@@ -282,6 +311,8 @@ class GlobusOperations(object):
|
|
|
282
311
|
working_dir (str): Current working directory, used for finding
|
|
283
312
|
relative paths and printing messages.
|
|
284
313
|
recursive: bool, If True, download directories recursively
|
|
314
|
+
no_compare (bool): If False (default), compare checksum to skip overwriting equivalent
|
|
315
|
+
files. If True, transfer without checking.
|
|
285
316
|
label: str, Globus transfer label to make finding tasks simpler
|
|
286
317
|
force: bool, If True force download even if local file exists
|
|
287
318
|
|
|
@@ -295,7 +326,26 @@ class GlobusOperations(object):
|
|
|
295
326
|
files_data, dirs_data, child_data, non_existing = treefuncs.treecompare(proj, paths, checksum=True, localtree=localtree, remotetree=remotetree)
|
|
296
327
|
|
|
297
328
|
# https://globus-sdk-python.readthedocs.io/en/stable/clients/transfer/#globus_sdk.TransferData
|
|
298
|
-
|
|
329
|
+
sync_level = "checksum"
|
|
330
|
+
if no_compare is True:
|
|
331
|
+
sync_level = None
|
|
332
|
+
|
|
333
|
+
consent_required_scopes = []
|
|
334
|
+
consent_required = check_for_consent_required(self.tc, self.local_endpoint_id)
|
|
335
|
+
if consent_required is not None:
|
|
336
|
+
consent_required_scopes.extend(consent_required)
|
|
337
|
+
|
|
338
|
+
consent_required = check_for_consent_required(self.tc, download.globus_endpoint_id)
|
|
339
|
+
if consent_required is not None:
|
|
340
|
+
consent_required_scopes.extend(consent_required)
|
|
341
|
+
|
|
342
|
+
if consent_required_scopes:
|
|
343
|
+
print("One or more of the endpoints requires consent in order to be used.\n"
|
|
344
|
+
"You must login a second time to grant consents.\n\n")
|
|
345
|
+
tr = get_transfer_rt_or_login(scopes=consent_required_scopes, reauth=True)
|
|
346
|
+
self.tc = make_transfer_client(tr)
|
|
347
|
+
|
|
348
|
+
tdata = globus_sdk.TransferData(self.tc, download.globus_endpoint_id, self.local_endpoint_id, label=label, sync_level=sync_level)
|
|
299
349
|
|
|
300
350
|
# add items
|
|
301
351
|
n_items = 0
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import imp
|
|
3
2
|
import os
|
|
4
3
|
import sys
|
|
5
4
|
from io import StringIO
|
|
@@ -9,10 +8,9 @@ import materials_commons.api as mcapi
|
|
|
9
8
|
from materials_commons.cli.subcommands.clone import clone_subcommand
|
|
10
9
|
from materials_commons.cli.subcommands.config import config_subcommand
|
|
11
10
|
from materials_commons.cli.subcommands.dataset import DatasetSubcommand
|
|
12
|
-
# from materials_commons.cli.subcommands.diff import diff_subcommand
|
|
13
11
|
from materials_commons.cli.subcommands.down import down_subcommand
|
|
14
12
|
from materials_commons.cli.subcommands.expt import ExptSubcommand
|
|
15
|
-
|
|
13
|
+
from materials_commons.cli.subcommands.fetch import fetch_subcommand
|
|
16
14
|
from materials_commons.cli.subcommands.init import init_subcommand
|
|
17
15
|
from materials_commons.cli.subcommands.ls import ls_subcommand
|
|
18
16
|
from materials_commons.cli.subcommands.mkdir import mkdir_subcommand
|
|
@@ -42,24 +40,18 @@ standard_usage = [
|
|
|
42
40
|
{'name': 'mkdir', 'desc': 'Make directories', 'subcommand': mkdir_subcommand},
|
|
43
41
|
{'name': 'rm', 'desc': 'Remove files and directories', 'subcommand': rm_subcommand},
|
|
44
42
|
{'name': 'mv', 'desc': 'Move files', 'subcommand': mv_subcommand},
|
|
45
|
-
|
|
46
|
-
# {'name': 'fetch', 'desc': 'Remote data fetching and configuration', 'subcommand': fetch_subcommand},
|
|
43
|
+
{'name': 'fetch', 'desc': 'Remote data fetching and configuration', 'subcommand': fetch_subcommand},
|
|
47
44
|
{'name': 'up', 'desc': 'Upload files', 'subcommand': up_subcommand},
|
|
48
45
|
{'name': 'down', 'desc': 'Download files', 'subcommand': down_subcommand},
|
|
49
46
|
{'name': 'globus', 'desc': 'Manage Globus uploads and downloads', 'subcommand': globus_subcommand},
|
|
50
47
|
{'name': 'versions', 'desc': 'List file versions', 'subcommand': versions_subcommand},
|
|
51
|
-
# {'name': 'templates', 'desc': 'List process templates', 'subcommand': TemplatesSubcommand()},
|
|
52
48
|
# {'name': 'proc', 'desc': 'List processes', 'subcommand': ProcSubcommand()},
|
|
53
49
|
# {'name': 'samp', 'desc': 'List samples', 'subcommand': SampSubcommand()},
|
|
54
50
|
{'name': 'config', 'desc': 'Configure `mc`', 'subcommand': config_subcommand}
|
|
55
51
|
]
|
|
56
52
|
standard_interfaces = {d['name']: d for d in standard_usage}
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
# {'name': 'actions', 'desc': 'List REST API actions', 'subcommand': ActionsSubcommand()}
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
def make_parser(custom_interfaces={}, developer_interfaces={}):
|
|
54
|
+
def make_parser():
|
|
63
55
|
|
|
64
56
|
usage_help = StringIO()
|
|
65
57
|
usage_help.write("mc <command> [<args>]\n\n")
|
|
@@ -68,18 +60,6 @@ def make_parser(custom_interfaces={}, developer_interfaces={}):
|
|
|
68
60
|
for name, interface in standard_interfaces.items():
|
|
69
61
|
usage_help.write(" {:10} {:40}\n".format(name, interface['desc']))
|
|
70
62
|
|
|
71
|
-
# read custom interfaces from config file
|
|
72
|
-
if len(custom_interfaces):
|
|
73
|
-
usage_help.write("\nSpecialized commands are:\n")
|
|
74
|
-
for name, interface in custom_interfaces.items():
|
|
75
|
-
usage_help.write(" {:10} {:40}\n".format(name, interface['desc']))
|
|
76
|
-
|
|
77
|
-
# hide from most users
|
|
78
|
-
if len(developer_interfaces):
|
|
79
|
-
usage_help.write("\nDeveloper commands are:\n")
|
|
80
|
-
for name, interface in developer_interfaces.items():
|
|
81
|
-
usage_help.write(" {:10} {:40}\n".format(name, interface['desc']))
|
|
82
|
-
|
|
83
63
|
parser = argparse.ArgumentParser(
|
|
84
64
|
description='Materials Commons command line interface',
|
|
85
65
|
usage=usage_help.getvalue())
|
|
@@ -100,13 +80,7 @@ def main(argv=None, working_dir=None):
|
|
|
100
80
|
mcapi.Client.set_debug_on()
|
|
101
81
|
pass
|
|
102
82
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
developer_interfaces = {}
|
|
106
|
-
if config.developer_mode:
|
|
107
|
-
developer_interfaces = {d['name']: d for d in developer_usage}
|
|
108
|
-
|
|
109
|
-
parser = make_parser(custom_interfaces, developer_interfaces)
|
|
83
|
+
parser = make_parser()
|
|
110
84
|
|
|
111
85
|
if len(argv) < 2:
|
|
112
86
|
parser.print_help()
|
|
@@ -120,23 +94,6 @@ def main(argv=None, working_dir=None):
|
|
|
120
94
|
result = standard_interfaces[args.command]['subcommand'](argv[2:], working_dir)
|
|
121
95
|
return result
|
|
122
96
|
|
|
123
|
-
elif args.command in custom_interfaces:
|
|
124
|
-
# load module and run command
|
|
125
|
-
modulename = custom_interfaces[args.command]['module']
|
|
126
|
-
subcommandname = custom_interfaces[args.command]['subcommand']
|
|
127
|
-
f, filename, description = imp.find_module(modulename)
|
|
128
|
-
try:
|
|
129
|
-
module = imp.load_module(modulename, f, filename, description)
|
|
130
|
-
result = getattr(module, subcommandname)(argv[2:], working_dir)
|
|
131
|
-
finally:
|
|
132
|
-
if f:
|
|
133
|
-
f.close()
|
|
134
|
-
return result
|
|
135
|
-
|
|
136
|
-
elif args.command in developer_interfaces:
|
|
137
|
-
result = developer_interfaces[args.command]['subcommand'](argv[2:], working_dir)
|
|
138
|
-
return result
|
|
139
|
-
|
|
140
97
|
else:
|
|
141
98
|
print('Unrecognized command')
|
|
142
99
|
parser.print_help()
|
|
@@ -147,7 +147,7 @@ class DatasetSubcommand(ListObjects):
|
|
|
147
147
|
def print_details(self, obj, args, out=sys.stdout):
|
|
148
148
|
# TODO: fix this
|
|
149
149
|
out.write("** WARNING: `mc dataset --details` is under development, some dataset attributes (for example: tags) may appear as 'null' even if they do exist. **\n")
|
|
150
|
-
if args.all:
|
|
150
|
+
if args.all or not clifuncs.project_exists(self.working_dir):
|
|
151
151
|
client = self.get_remote(args)
|
|
152
152
|
if args.file_selection:
|
|
153
153
|
out.write("** NOTE: --file-selection: Not available for public datasets **\n")
|
|
@@ -191,13 +191,18 @@ class DatasetSubcommand(ListObjects):
|
|
|
191
191
|
|
|
192
192
|
.. note:: The downloaded dataset is named dataset.<dataset_uuid>.zip
|
|
193
193
|
"""
|
|
194
|
-
|
|
194
|
+
|
|
195
|
+
if args.all or not clifuncs.project_exists(self.working_dir):
|
|
196
|
+
remote = self.get_remote(args)
|
|
197
|
+
else:
|
|
198
|
+
proj = clifuncs.make_local_project(self.working_dir)
|
|
199
|
+
remote = proj.remote
|
|
195
200
|
for obj in objects:
|
|
196
201
|
self.print_details(obj, args, out=out)
|
|
197
202
|
out.write("Downloading...\n")
|
|
198
203
|
dataset_id = obj.id
|
|
199
204
|
to = "dataset." + obj.uuid + ".zip"
|
|
200
|
-
|
|
205
|
+
remote.download_published_dataset_zipfile(dataset_id, to)
|
|
201
206
|
out.write("DONE\n\n")
|
|
202
207
|
return
|
|
203
208
|
|
|
@@ -300,7 +300,7 @@ def down_subcommand(argv, working_dir):
|
|
|
300
300
|
+ " not ready for downloading. Materials Commons is still preparing the project"
|
|
301
301
|
+ " files for download. For large projects this may take some time.")
|
|
302
302
|
print("Use `mc globus download` to check when it is ready and try again.")
|
|
303
|
-
|
|
303
|
+
return
|
|
304
304
|
|
|
305
305
|
print("Download is ready.")
|
|
306
306
|
|
|
@@ -310,9 +310,8 @@ def down_subcommand(argv, working_dir):
|
|
|
310
310
|
|
|
311
311
|
globus_ops = cliglobus.GlobusOperations()
|
|
312
312
|
task_id = globus_ops.download_v0(proj, paths, download, working_dir,
|
|
313
|
-
recursive=args.recursive, label=label,
|
|
314
|
-
|
|
315
|
-
remotetree=remotetree,
|
|
313
|
+
recursive=args.recursive, label=label, localtree=localtree,
|
|
314
|
+
remotetree=remotetree, no_compare=args.no_compare,
|
|
316
315
|
force=args.force)
|
|
317
316
|
|
|
318
317
|
if task_id:
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
import materials_commons.api as mcapi
|
|
7
|
+
import materials_commons.cli.functions as clifuncs
|
|
8
|
+
import materials_commons.cli.tree_functions as treefuncs
|
|
9
|
+
import materials_commons.cli.file_functions as filefuncs
|
|
10
|
+
from materials_commons.cli.treedb import LocalTree, RemoteTree
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_fetch_status(pconfig):
|
|
14
|
+
if pconfig.remote_updatetime:
|
|
15
|
+
print("Fetch lock set at:", clifuncs.format_time(pconfig.remote_updatetime))
|
|
16
|
+
print("Project data will at least reflect remote changes that happened before this time.")
|
|
17
|
+
print("**Changes that happen after this time may not be reflected in CLI output**.")
|
|
18
|
+
print("Do `mc fetch --unlock` to turn off the fetch lock.")
|
|
19
|
+
else:
|
|
20
|
+
print("Fetch lock off")
|
|
21
|
+
print("Project data will always be fetched from remote.")
|
|
22
|
+
|
|
23
|
+
def make_parser():
|
|
24
|
+
desc = "By default, remote data is fetched for every request. To be more efficient, some remote data can be fetched and cached using `mc fetch --lock`. When 'locked', remote data is recovered from a local cache if it has been updated since the lock was put in place. Any data fetched before the lock was put in place is still updated as necessary. To the extent possible, the cache will be updated when the remote is modified via the `mc` command line. While the lock is in place, use `mc fetch <path>` to force the update of data for particular files or directories. To reset the lock time, use `mc fetch --lock` again. To stop caching, use `mc fetch --unlock`."
|
|
25
|
+
|
|
26
|
+
parser = argparse.ArgumentParser(
|
|
27
|
+
description=desc,
|
|
28
|
+
prog='mc fetch')
|
|
29
|
+
parser.add_argument('paths', nargs='*', default=[], help='Files or directories')
|
|
30
|
+
parser.add_argument('-r', '--recursive', action="store_true", default=False, help='Fetch data recursively for specified paths.')
|
|
31
|
+
parser.add_argument('--verbose', action="store_true", default=False, help='Print verbosely.')
|
|
32
|
+
parser.add_argument('--no-children', action="store_true", default=False, help='Do not update data for children of directories.')
|
|
33
|
+
parser.add_argument('--lock', action="store_true", default=False, help='Only fetch data to replace records older than now.')
|
|
34
|
+
parser.add_argument('--unlock', action="store_true", default=False, help='Always fetch data.')
|
|
35
|
+
parser.add_argument('--status', action="store_true", default=False, help='Display fetch lock status.')
|
|
36
|
+
return parser
|
|
37
|
+
|
|
38
|
+
def fetch_subcommand(argv, working_dir):
|
|
39
|
+
"""
|
|
40
|
+
Fetch remote data
|
|
41
|
+
|
|
42
|
+
mc fetch [--recursive] [<path>...]
|
|
43
|
+
mc fetch --lock
|
|
44
|
+
mc fetch --unlock
|
|
45
|
+
mc fetch --status
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
parser = make_parser()
|
|
49
|
+
args = parser.parse_args(argv)
|
|
50
|
+
|
|
51
|
+
proj = clifuncs.make_local_project(working_dir)
|
|
52
|
+
pconfig = clifuncs.read_project_config(proj.local_path)
|
|
53
|
+
|
|
54
|
+
if args.lock:
|
|
55
|
+
pconfig.remote_updatetime = time.time()
|
|
56
|
+
pconfig.save()
|
|
57
|
+
print_fetch_status(pconfig)
|
|
58
|
+
|
|
59
|
+
elif args.unlock:
|
|
60
|
+
pconfig.remote_updatetime = None
|
|
61
|
+
pconfig.save()
|
|
62
|
+
print_fetch_status(pconfig)
|
|
63
|
+
|
|
64
|
+
elif args.status:
|
|
65
|
+
print_fetch_status(pconfig)
|
|
66
|
+
|
|
67
|
+
else:
|
|
68
|
+
if not args.paths:
|
|
69
|
+
print("Nothing to fetch")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
remotetree = RemoteTree(proj, time.time())
|
|
73
|
+
refpath = os.path.dirname(proj.local_path)
|
|
74
|
+
|
|
75
|
+
get_children = True
|
|
76
|
+
if args.no_children:
|
|
77
|
+
get_children = False
|
|
78
|
+
|
|
79
|
+
mcpaths = treefuncs.clipaths_to_mcpaths(proj.local_path, args.paths,
|
|
80
|
+
working_dir)
|
|
81
|
+
|
|
82
|
+
remotetree.connect()
|
|
83
|
+
for mcpath in mcpaths:
|
|
84
|
+
print("fetch:", mcpath)
|
|
85
|
+
remotetree.update(
|
|
86
|
+
mcpath,
|
|
87
|
+
get_children=get_children,
|
|
88
|
+
recurs=args.recursive,
|
|
89
|
+
verbose=args.verbose)
|
|
90
|
+
remotetree.close()
|
|
91
|
+
|
|
92
|
+
return
|
|
@@ -59,9 +59,13 @@ class GlobusUploadTaskSubcommand(ListObjects):
|
|
|
59
59
|
upload.project_id = upload._data["project_id"]
|
|
60
60
|
status_code = upload._data["status"]
|
|
61
61
|
if status_code == 0:
|
|
62
|
+
status_message = "Done" # user should not see this
|
|
63
|
+
elif status_code == 1:
|
|
62
64
|
status_message = "Finishing" # processing uploaded files into project
|
|
63
65
|
elif status_code == 2:
|
|
64
|
-
status_message = "Ready"
|
|
66
|
+
status_message = "Ready" # user may upload
|
|
67
|
+
elif status_code == 3:
|
|
68
|
+
status_message = "Not started" # user should not see this
|
|
65
69
|
else:
|
|
66
70
|
status_message = "Error - Unknown status code: " + str(status_code)
|
|
67
71
|
upload.status_code = status_code
|
|
@@ -284,7 +288,8 @@ class GlobusDownloadTaskSubcommand(ListObjects):
|
|
|
284
288
|
headers=['', 'project_name', 'project_id', 'type', 'name', 'id', 'created', 'status'],
|
|
285
289
|
deletable=True,
|
|
286
290
|
creatable=True,
|
|
287
|
-
|
|
291
|
+
custom_actions=['unset'],
|
|
292
|
+
custom_selection_actions=['goto', 'set'],
|
|
288
293
|
request_confirmation_actions={
|
|
289
294
|
'goto': 'You want to goto these downloads in a web browser?'
|
|
290
295
|
}
|
{materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/ls.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import copy
|
|
3
|
+
import json
|
|
3
4
|
import os
|
|
4
5
|
import sys
|
|
5
6
|
import time
|
|
@@ -119,9 +120,11 @@ def _ls_print(proj, data, refpath=None, printjson=False, checksum=False, checkds
|
|
|
119
120
|
headers += ['selected', 'selected_by']
|
|
120
121
|
|
|
121
122
|
if printjson:
|
|
123
|
+
output = []
|
|
122
124
|
for path, record in data.items():
|
|
123
125
|
if record['r_obj']:
|
|
124
|
-
|
|
126
|
+
output.append(record['r_obj']._data)
|
|
127
|
+
print(json.dumps(output))
|
|
125
128
|
else:
|
|
126
129
|
path_data = _format_path_data(proj, data, columns, refpath=refpath, checksum=checksum)
|
|
127
130
|
if len(path_data):
|
{materials_commons-cli-2.0b9 → materials_commons-cli-2.1.1}/materials_commons/cli/subcommands/up.py
RENAMED
|
@@ -74,8 +74,15 @@ def up_subcommand(argv, working_dir):
|
|
|
74
74
|
|
|
75
75
|
if args.globus:
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
# convert input paths (absolute or relative to working_dir) to local_abspath
|
|
78
|
+
local_abspaths = treefuncs.clipaths_to_local_abspaths(
|
|
79
|
+
proj.local_path, args.paths, working_dir)
|
|
80
|
+
|
|
81
|
+
# filter, skipping .mc, those specified by .mcignore
|
|
82
|
+
local_abspaths = treefuncs.filter_local_abspaths(
|
|
83
|
+
proj.local_path, local_abspaths, working_dir)
|
|
84
|
+
|
|
85
|
+
mcpaths = treefuncs.clipaths_to_mcpaths(proj.local_path, local_abspaths, working_dir)
|
|
79
86
|
|
|
80
87
|
all_uploads = {upload.id:upload for upload in proj.remote.get_all_globus_upload_requests(proj.id)}
|
|
81
88
|
|
|
@@ -104,7 +111,8 @@ def up_subcommand(argv, working_dir):
|
|
|
104
111
|
|
|
105
112
|
globus_ops = cliglobus.GlobusOperations()
|
|
106
113
|
task_id = globus_ops.upload_v0(proj, mcpaths, upload, working_dir,
|
|
107
|
-
recursive=args.recursive,
|
|
114
|
+
recursive=args.recursive, no_compare=args.no_compare,
|
|
115
|
+
label=label)
|
|
108
116
|
|
|
109
117
|
if task_id:
|
|
110
118
|
print("Globus transfer task initiated.")
|
|
@@ -119,7 +127,7 @@ def up_subcommand(argv, working_dir):
|
|
|
119
127
|
if not args.no_compare:
|
|
120
128
|
localtree = LocalTree(proj.local_path)
|
|
121
129
|
|
|
122
|
-
treefuncs.
|
|
130
|
+
treefuncs.standard_upload_v2(proj, args.paths, working_dir,
|
|
123
131
|
recursive=args.recursive, limit=args.limit[0],
|
|
124
132
|
no_compare=args.no_compare,
|
|
125
133
|
upload_as=upload_as, localtree=localtree,
|