qmenta-sdk-lib 2.2.dev3493__tar.gz → 2.2.dev3494__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.
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/PKG-INFO +1 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/pyproject.toml +1 -1
- qmenta_sdk_lib-2.2.dev3494/python/qmenta/sdk/__init__.py +2 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/bids/wrapper.py +9 -5
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/client.py +4 -2
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/communication.py +21 -17
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/context.py +3 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/directory_utils.py +2 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/executor.py +8 -4
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/init.py +10 -6
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/client.py +2 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/context.py +60 -32
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/executor.py +12 -6
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/log_capture.py +13 -7
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/make_entrypoint.py +2 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/inputs.py +2 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/integration_ui.py +64 -12
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/integration_workflow_ui.py +89 -55
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/launch_gui.py +10 -3
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/make_files.py +4 -2
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/outputs.py +8 -2
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/run_test_docker.py +11 -5
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/tool_maker.py +127 -69
- qmenta_sdk_lib-2.2.dev3493/python/qmenta/sdk/__init__.py +0 -1
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/bids/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/bids/make_entrypoint.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/parse_settings.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/context.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/file_filter.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/modalities.py +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/templates_tool_maker/Dockerfile_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/templates_tool_maker/description_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/templates_tool_maker/qmenta.png +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/templates_tool_maker/test_tool_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/templates_tool_maker/tool_schema +0 -0
|
@@ -83,7 +83,8 @@ def generate_file_path(original_name, sub_name, modality_name, cat_dir):
|
|
|
83
83
|
|
|
84
84
|
def format_input_data(context, subject, session, path):
|
|
85
85
|
"""
|
|
86
|
-
Format input data followings the BIDS specification:
|
|
86
|
+
Format input data followings the BIDS specification:
|
|
87
|
+
http://bids.neuroimaging.io/bids_spec.pdf
|
|
87
88
|
|
|
88
89
|
:param context: AnalysisContext of the running analysis
|
|
89
90
|
:param subject: Subject name (from analysis_data)
|
|
@@ -97,7 +98,8 @@ def format_input_data(context, subject, session, path):
|
|
|
97
98
|
|
|
98
99
|
dataset_description = {"Name": "QMENTA_ANALYSIS", "BIDSVersion": "1.1.0"}
|
|
99
100
|
|
|
100
|
-
# The input container specification must include file filters following
|
|
101
|
+
# The input container specification must include file filters following
|
|
102
|
+
# the bids_ff_names nomenclature
|
|
101
103
|
bids_ff_names = {
|
|
102
104
|
"anat": [
|
|
103
105
|
"T1w",
|
|
@@ -155,9 +157,10 @@ def format_input_data(context, subject, session, path):
|
|
|
155
157
|
|
|
156
158
|
if os.path.isfile(target_path):
|
|
157
159
|
logger.warning(
|
|
158
|
-
"Multiple files with same name ({}).
|
|
160
|
+
"Multiple files with same name ({}).".format(
|
|
159
161
|
target_path
|
|
160
|
-
)
|
|
162
|
+
),
|
|
163
|
+
" Only the first will be used"
|
|
161
164
|
)
|
|
162
165
|
break
|
|
163
166
|
|
|
@@ -211,7 +214,8 @@ def run(context):
|
|
|
211
214
|
mkdirs(input_path)
|
|
212
215
|
mkdirs(output_path)
|
|
213
216
|
|
|
214
|
-
# Download data from the platform and store following
|
|
217
|
+
# Download data from the platform and store following
|
|
218
|
+
# the BIDS specification
|
|
215
219
|
format_input_data(
|
|
216
220
|
context,
|
|
217
221
|
subject=analysis_data["patient_secret_name"],
|
|
@@ -28,7 +28,8 @@ class ExecClient(object):
|
|
|
28
28
|
Parameters
|
|
29
29
|
----------
|
|
30
30
|
user_script_path : str
|
|
31
|
-
Should look like 'path.to.module:object', where 'object' should be
|
|
31
|
+
Should look like 'path.to.module:object', where 'object' should be
|
|
32
|
+
an importable analysis tool class
|
|
32
33
|
or a function taking one 'context' argument.
|
|
33
34
|
"""
|
|
34
35
|
|
|
@@ -70,7 +71,8 @@ class ExecClient(object):
|
|
|
70
71
|
Parameters
|
|
71
72
|
----------
|
|
72
73
|
state : AnalysisState
|
|
73
|
-
One of AnalysisState.RUNNING, AnalysisState.COMPLETED,
|
|
74
|
+
One of AnalysisState.RUNNING, AnalysisState.COMPLETED,
|
|
75
|
+
AnalysisState.EXCEPTION or AnalysisState.NO_FILES
|
|
74
76
|
kwargs : dict
|
|
75
77
|
"""
|
|
76
78
|
assert isinstance(
|
{qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/communication.py
RENAMED
|
@@ -33,15 +33,17 @@ class CommunicationObject(object):
|
|
|
33
33
|
token : str
|
|
34
34
|
Unique token that authenticates the communication
|
|
35
35
|
timeout : float or tuple, optional
|
|
36
|
-
How many seconds to wait for the server to send data before giving
|
|
37
|
-
(connect timeout, read timeout) tuple.
|
|
36
|
+
How many seconds to wait for the server to send data before giving
|
|
37
|
+
up, as a float, or a (connect timeout, read timeout) tuple.
|
|
38
|
+
Defaults to
|
|
38
39
|
chunk_size : int
|
|
39
40
|
Size of chunks in bytes (1MB = 1024KB, 1KB = 1024B)
|
|
40
41
|
verify : bool, optional
|
|
41
42
|
Controls whether the server's TLS certificate is verified.
|
|
42
43
|
Defaults to True.
|
|
43
44
|
critical: bool, optional
|
|
44
|
-
Overrides total_retry seting in requests by making themm fail
|
|
45
|
+
Overrides total_retry seting in requests by making themm fail
|
|
46
|
+
after the first error if set.
|
|
45
47
|
"""
|
|
46
48
|
self.timeout = timeout
|
|
47
49
|
self.chunk_size = chunk_size
|
|
@@ -60,7 +62,8 @@ class CommunicationObject(object):
|
|
|
60
62
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
61
63
|
ret = self.send_request("logout")
|
|
62
64
|
if not ret["success"]:
|
|
63
|
-
# Not being able to logout is a security issue and must be a
|
|
65
|
+
# Not being able to logout is a security issue and must be a
|
|
66
|
+
# fatal error
|
|
64
67
|
raise RuntimeError("Unable to logout")
|
|
65
68
|
|
|
66
69
|
def send_request(
|
|
@@ -82,15 +85,15 @@ class CommunicationObject(object):
|
|
|
82
85
|
Parameters
|
|
83
86
|
----------
|
|
84
87
|
access_point : str, optional
|
|
85
|
-
API endpoint. Will be joined with the request URL provided at
|
|
86
|
-
full ("absolute") url
|
|
88
|
+
API endpoint. Will be joined with the request URL provided at
|
|
89
|
+
construction time to create the full ("absolute") url
|
|
87
90
|
req_data : dict or str, optional
|
|
88
91
|
Request data
|
|
89
92
|
req_headers : dict, optional
|
|
90
93
|
Request headers
|
|
91
94
|
return_json : bool, optional
|
|
92
|
-
Whether the response should be a JSON object or a requests.
|
|
93
|
-
Defaults to True.
|
|
95
|
+
Whether the response should be a JSON object or a requests.
|
|
96
|
+
Response instance. Defaults to True.
|
|
94
97
|
timeout : float, optional
|
|
95
98
|
Timeout in seconds for this request.
|
|
96
99
|
Defaults to the timeout set at construction time.
|
|
@@ -100,7 +103,8 @@ class CommunicationObject(object):
|
|
|
100
103
|
stream : bool, optional
|
|
101
104
|
If False, the response content will be immediately downloaded.
|
|
102
105
|
url : str, optional
|
|
103
|
-
When no access_point is provided, a full url must be provided
|
|
106
|
+
When no access_point is provided, a full url must be provided
|
|
107
|
+
through this argument
|
|
104
108
|
kwargs : dict
|
|
105
109
|
Additional arguments to be passed to `requests.Session.post`
|
|
106
110
|
method: str
|
|
@@ -109,8 +113,8 @@ class CommunicationObject(object):
|
|
|
109
113
|
Returns
|
|
110
114
|
-------
|
|
111
115
|
requests.Response or dict
|
|
112
|
-
JSON response encoded in a dictionary if return_json is True
|
|
113
|
-
otherwise requests.Response object.
|
|
116
|
+
JSON response encoded in a dictionary if return_json is True
|
|
117
|
+
(default), otherwise requests.Response object.
|
|
114
118
|
"""
|
|
115
119
|
logger = logging.getLogger(__name__)
|
|
116
120
|
|
|
@@ -179,8 +183,8 @@ class CommunicationObject(object):
|
|
|
179
183
|
Parameters
|
|
180
184
|
----------
|
|
181
185
|
access_point : str
|
|
182
|
-
API endpoint. Will be joined with the request URL provided at
|
|
183
|
-
full ("absolute") url
|
|
186
|
+
API endpoint. Will be joined with the request URL provided at
|
|
187
|
+
construction time to create the full ("absolute") url
|
|
184
188
|
files : dict
|
|
185
189
|
Request files
|
|
186
190
|
req_data : dict or str, optional
|
|
@@ -188,8 +192,8 @@ class CommunicationObject(object):
|
|
|
188
192
|
req_headers : dict, optional
|
|
189
193
|
Request headers
|
|
190
194
|
return_json : bool, optional
|
|
191
|
-
Whether the response should be a JSON object or a requests.
|
|
192
|
-
Defaults to True.
|
|
195
|
+
Whether the response should be a JSON object or a requests.
|
|
196
|
+
Response instance. Defaults to True.
|
|
193
197
|
timeout : float, optional
|
|
194
198
|
Timeout in seconds for this request.
|
|
195
199
|
Defaults to the timeout set at construction time.
|
|
@@ -204,8 +208,8 @@ class CommunicationObject(object):
|
|
|
204
208
|
Returns
|
|
205
209
|
-------
|
|
206
210
|
requests.Response or dict
|
|
207
|
-
JSON response encoded in a dictionary if return_json is True
|
|
208
|
-
otherwise requests.Response object.
|
|
211
|
+
JSON response encoded in a dictionary if return_json is True
|
|
212
|
+
(default), otherwise requests.Response object.
|
|
209
213
|
"""
|
|
210
214
|
logger = logging.getLogger(__name__)
|
|
211
215
|
|
|
@@ -973,7 +973,9 @@ class AnalysisContext:
|
|
|
973
973
|
"replace_flag": replace.value,
|
|
974
974
|
"tags": ",".join(tags),
|
|
975
975
|
"file_info": json.dumps(file_info),
|
|
976
|
-
"file_format": file_format or self.__detect_file_format(
|
|
976
|
+
"file_format": file_format or self.__detect_file_format(
|
|
977
|
+
source_file_path
|
|
978
|
+
),
|
|
977
979
|
"protocol": str(
|
|
978
980
|
protocol or file_name.split(".")[0]
|
|
979
981
|
if "." in file_name
|
|
@@ -63,7 +63,8 @@ def parse_args():
|
|
|
63
63
|
argp.add_argument(
|
|
64
64
|
"--tool-path",
|
|
65
65
|
required=True,
|
|
66
|
-
help="Path to the Python tool (package.module:function)
|
|
66
|
+
help="Path to the Python tool (package.module:function)"
|
|
67
|
+
"to be executed",
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
# verbosity
|
|
@@ -76,8 +77,10 @@ def parse_args():
|
|
|
76
77
|
"--chunk-size",
|
|
77
78
|
type=str,
|
|
78
79
|
default=default_chunk_size,
|
|
79
|
-
help="Size of each chunk in download/upload operations,
|
|
80
|
-
"
|
|
80
|
+
help="Size of each chunk in download/upload operations,"
|
|
81
|
+
"expressed in a human readable data size. "
|
|
82
|
+
"(e.g. 256 --> 256 bytes ; 512B --> 512 bytes ;"
|
|
83
|
+
"1MiB --> 1048576 bytes; 1MB --> 1000000",
|
|
81
84
|
)
|
|
82
85
|
|
|
83
86
|
# only for testing purposes
|
|
@@ -95,7 +98,8 @@ def parse_args():
|
|
|
95
98
|
nargs="?",
|
|
96
99
|
const=True,
|
|
97
100
|
default=False,
|
|
98
|
-
help="Ignore requests total_retry number and fail after each first
|
|
101
|
+
help="Ignore requests total_retry number and fail after each first"
|
|
102
|
+
"error",
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
# metadata
|
|
@@ -52,9 +52,12 @@ def run(context):
|
|
|
52
52
|
for file_handler in input_files:
|
|
53
53
|
file_handler.download('/root/') # Keeping the original names
|
|
54
54
|
|
|
55
|
-
# Call the MATLAB helper script with the path of the MATLAB runtime
|
|
56
|
-
#
|
|
57
|
-
#
|
|
55
|
+
# Call the MATLAB helper script with the path of the MATLAB runtime
|
|
56
|
+
# installation.
|
|
57
|
+
# Make sure your script finalizes. Otherwise this call will be waiting
|
|
58
|
+
# forever.
|
|
59
|
+
# Alternatively, you can always implement a timeout
|
|
60
|
+
# (e.g. with subprocess)
|
|
58
61
|
|
|
59
62
|
# os.system('/root/MATLAB_run_script.sh /opt/mcr/v92')
|
|
60
63
|
|
|
@@ -68,8 +71,8 @@ def run(context):
|
|
|
68
71
|
analysis_data = context.fetch_analysis_data()
|
|
69
72
|
|
|
70
73
|
# Get the complete list of input file handlers
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
# You can add fitlers (modality, tags...)
|
|
75
|
+
input_files = context.get_files('input')
|
|
73
76
|
# Download them
|
|
74
77
|
for file_handler in input_files:
|
|
75
78
|
file_handler.download('/root/')
|
|
@@ -78,7 +81,8 @@ def run(context):
|
|
|
78
81
|
# ...
|
|
79
82
|
|
|
80
83
|
# And upload the results
|
|
81
|
-
# context.upload_file(path, path_in_output)
|
|
84
|
+
# context.upload_file(path, path_in_output)
|
|
85
|
+
# # You can add filters (modality, tags...)
|
|
82
86
|
"""
|
|
83
87
|
|
|
84
88
|
mrinfo_mrtrix_default_tool = """
|
|
@@ -16,7 +16,8 @@ class LocalExecClient(ExecClient): # Override main execution client
|
|
|
16
16
|
Parameters
|
|
17
17
|
----------
|
|
18
18
|
state : AnalysisState
|
|
19
|
-
One of AnalysisState.RUNNING, AnalysisState.COMPLETED,
|
|
19
|
+
One of AnalysisState.RUNNING, AnalysisState.COMPLETED,
|
|
20
|
+
AnalysisState.EXCEPTION or AnalysisState.NO_FILES
|
|
20
21
|
kwargs : dict
|
|
21
22
|
"""
|
|
22
23
|
logging.getLogger(__name__).info(
|
{qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/context.py
RENAMED
|
@@ -30,7 +30,8 @@ class LocalAnalysisContext:
|
|
|
30
30
|
|
|
31
31
|
def __init__(self, settings, src_folder, out_folder, res_folder):
|
|
32
32
|
"""
|
|
33
|
-
Context object that interfaces with the QMENTA platform.
|
|
33
|
+
Context object that interfaces with the QMENTA platform.
|
|
34
|
+
Should not be created directly.
|
|
34
35
|
"""
|
|
35
36
|
|
|
36
37
|
self.__settings = settings
|
|
@@ -45,14 +46,17 @@ class LocalAnalysisContext:
|
|
|
45
46
|
Returns
|
|
46
47
|
-------
|
|
47
48
|
dict
|
|
48
|
-
Dictionary that contains important information about
|
|
49
|
+
Dictionary that contains important information about
|
|
50
|
+
the current analysis, such as:
|
|
49
51
|
* state (str) : State of the analysis
|
|
50
52
|
* name (str) : Name of the analysis
|
|
51
53
|
* script_name (str) : Name of the script
|
|
52
|
-
* user_id (str) : Username of the user that started
|
|
54
|
+
* user_id (str) : Username of the user that started
|
|
55
|
+
the analysis
|
|
53
56
|
* patient_secret_name (str) : Name of the subject
|
|
54
57
|
* ssid (str) : Timepoint identifier
|
|
55
|
-
* settings (dict) : Settings dictionary.
|
|
58
|
+
* settings (dict) : Settings dictionary.
|
|
59
|
+
See documentation in method `get_settings`
|
|
56
60
|
* tags (list) : List of tags
|
|
57
61
|
* version (str) : Tool version
|
|
58
62
|
"""
|
|
@@ -83,11 +87,13 @@ class LocalAnalysisContext:
|
|
|
83
87
|
status : QCStatus
|
|
84
88
|
QCStatus.PASS or QCStatus.FAIL
|
|
85
89
|
comments : str, optional
|
|
86
|
-
Additional comments explaining why
|
|
90
|
+
Additional comments explaining why
|
|
91
|
+
the QC status has been set to pass or fail.
|
|
87
92
|
entity : QCEntity, optional
|
|
88
93
|
QCEntity.ANALYSIS or QCEntity.SESSION
|
|
89
94
|
input_id : str, optional
|
|
90
|
-
The id of the session in the settings definition when
|
|
95
|
+
The id of the session in the settings definition when
|
|
96
|
+
entity is QCEntity.SESSION
|
|
91
97
|
"""
|
|
92
98
|
logger = logging.getLogger(__name__)
|
|
93
99
|
|
|
@@ -148,7 +154,8 @@ class LocalAnalysisContext:
|
|
|
148
154
|
Parameters
|
|
149
155
|
----------
|
|
150
156
|
value : int, optional
|
|
151
|
-
Number between 0 and 100 indicating the current status of the
|
|
157
|
+
Number between 0 and 100 indicating the current status of the
|
|
158
|
+
execution
|
|
152
159
|
message : str, optional
|
|
153
160
|
Progress message.
|
|
154
161
|
"""
|
|
@@ -164,12 +171,14 @@ class LocalAnalysisContext:
|
|
|
164
171
|
value = int(value) # Cast to int to avoid float/string problems
|
|
165
172
|
if value > 100:
|
|
166
173
|
logger.warning(
|
|
167
|
-
"The progress value should be between 0 and 100
|
|
174
|
+
"The progress value should be between 0 and 100"
|
|
175
|
+
"(using 100 instead)"
|
|
168
176
|
)
|
|
169
177
|
value = 100
|
|
170
178
|
elif value < 0:
|
|
171
179
|
logger.warning(
|
|
172
|
-
"The progress value should be between 0 and 100
|
|
180
|
+
"The progress value should be between 0 and 100"
|
|
181
|
+
"(using 0 instead)"
|
|
173
182
|
)
|
|
174
183
|
value = 0
|
|
175
184
|
|
|
@@ -196,8 +205,8 @@ class LocalAnalysisContext:
|
|
|
196
205
|
subject_container_id=None,
|
|
197
206
|
):
|
|
198
207
|
"""
|
|
199
|
-
Returns the files in the input container specified by `input_id` that
|
|
200
|
-
and regular expression, if any.
|
|
208
|
+
Returns the files in the input container specified by `input_id` that
|
|
209
|
+
match the modality, tags and regular expression, if any.
|
|
201
210
|
|
|
202
211
|
Parameters
|
|
203
212
|
----------
|
|
@@ -207,12 +216,12 @@ class LocalAnalysisContext:
|
|
|
207
216
|
Optional modality, will allow any modalities if None given.
|
|
208
217
|
Default is None.
|
|
209
218
|
tags : set, optional
|
|
210
|
-
Optional set of tags, will allow any tags if None given, and will
|
|
211
|
-
the empty set) otherwise.
|
|
219
|
+
Optional set of tags, will allow any tags if None given, and will
|
|
220
|
+
match any set of tags (including the empty set) otherwise.
|
|
212
221
|
Default is None.
|
|
213
222
|
reg_expression : str, optional
|
|
214
|
-
Regular expression to match with the file names in the specified
|
|
215
|
-
if None given.
|
|
223
|
+
Regular expression to match with the file names in the specified
|
|
224
|
+
container, will allow any file names if None given.
|
|
216
225
|
Default is None.
|
|
217
226
|
file_filter_condition_name : str, optional
|
|
218
227
|
File filter specified in the settings of the tool.
|
|
@@ -235,8 +244,11 @@ class LocalAnalysisContext:
|
|
|
235
244
|
# Create File
|
|
236
245
|
for file_description in self.__settings[input_id]:
|
|
237
246
|
if "path" not in file_description:
|
|
238
|
-
msg =
|
|
239
|
-
|
|
247
|
+
msg = (
|
|
248
|
+
'Cannot find the key "path" '
|
|
249
|
+
'for the input container {}'.format(
|
|
250
|
+
input_id
|
|
251
|
+
)
|
|
240
252
|
)
|
|
241
253
|
logger.error(msg)
|
|
242
254
|
raise RuntimeError(msg)
|
|
@@ -343,7 +355,8 @@ class LocalAnalysisContext:
|
|
|
343
355
|
|
|
344
356
|
def download_resource(self, resource_path, destination_path):
|
|
345
357
|
"""
|
|
346
|
-
Downloads a file from the user/group tool resources.
|
|
358
|
+
Downloads a file from the user/group tool resources.
|
|
359
|
+
The resource path can include subdirectories and must
|
|
347
360
|
be relative:
|
|
348
361
|
Downloads a resource file to the container.
|
|
349
362
|
|
|
@@ -389,7 +402,8 @@ class LocalAnalysisContext:
|
|
|
389
402
|
|
|
390
403
|
def _get_manual_analysis_data(self):
|
|
391
404
|
"""
|
|
392
|
-
Returns a dictionary with the manual analysis data generated during
|
|
405
|
+
Returns a dictionary with the manual analysis data generated during
|
|
406
|
+
the manual step (user interaction)
|
|
393
407
|
|
|
394
408
|
Returns
|
|
395
409
|
-------
|
|
@@ -399,7 +413,8 @@ class LocalAnalysisContext:
|
|
|
399
413
|
logger = logging.getLogger(__name__)
|
|
400
414
|
logger.info("Getting manual analysis data")
|
|
401
415
|
logger.warning(
|
|
402
|
-
"Getting manual analysis data is not supported locally.
|
|
416
|
+
"Getting manual analysis data is not supported locally. "
|
|
417
|
+
"Skipping..."
|
|
403
418
|
)
|
|
404
419
|
return {}
|
|
405
420
|
|
|
@@ -424,7 +439,8 @@ class LocalAnalysisContext:
|
|
|
424
439
|
title : str, optional
|
|
425
440
|
How the metadata field should be presented
|
|
426
441
|
readonly : bool, optional
|
|
427
|
-
Whether the user should be able to edit the value on the platform
|
|
442
|
+
Whether the user should be able to edit the value on the platform
|
|
443
|
+
or not (only by analyses)
|
|
428
444
|
"""
|
|
429
445
|
logger = logging.getLogger(__name__)
|
|
430
446
|
|
|
@@ -476,12 +492,14 @@ class LocalAnalysisContext:
|
|
|
476
492
|
|
|
477
493
|
def _assure_metadata_parameters(self, params):
|
|
478
494
|
"""
|
|
479
|
-
Check whether a metadata parameter exists, creating it if not.
|
|
495
|
+
Check whether a metadata parameter exists, creating it if not.
|
|
496
|
+
Not supported for local testing.
|
|
480
497
|
|
|
481
498
|
Parameters
|
|
482
499
|
----------
|
|
483
500
|
params : dict
|
|
484
|
-
The definition of the parameter. It may contain "title", "type",
|
|
501
|
+
The definition of the parameter. It may contain "title", "type",
|
|
502
|
+
"visible", "readonly" or "order".
|
|
485
503
|
"""
|
|
486
504
|
logger = logging.getLogger(__name__)
|
|
487
505
|
logger.info("Setting metadata param (ckeck): {}".format(params))
|
|
@@ -509,7 +527,8 @@ class LocalAnalysisContext:
|
|
|
509
527
|
class LocalFile:
|
|
510
528
|
def __init__(self, name, input_container_path, modality, tags, file_info):
|
|
511
529
|
"""
|
|
512
|
-
Object that represents a file and all its metadata in the platform.
|
|
530
|
+
Object that represents a file and all its metadata in the platform.
|
|
531
|
+
Should not be created directly.
|
|
513
532
|
"""
|
|
514
533
|
self.name = name
|
|
515
534
|
self.__input_container_path = input_container_path
|
|
@@ -543,13 +562,16 @@ class LocalFile:
|
|
|
543
562
|
|
|
544
563
|
def get_file_info(self):
|
|
545
564
|
"""
|
|
546
|
-
Get the file information.
|
|
547
|
-
|
|
565
|
+
Get the file information.
|
|
566
|
+
The type of information depends on the type of file
|
|
567
|
+
(e.g. nifti files include information such as 'Data strides',
|
|
568
|
+
'Data type', 'Dimensions' or 'Voxel size').
|
|
548
569
|
|
|
549
570
|
Returns
|
|
550
571
|
-------
|
|
551
572
|
dict
|
|
552
|
-
Dictionary of key-value pairs that depend on the format of
|
|
573
|
+
Dictionary of key-value pairs that depend on the format of
|
|
574
|
+
the file.
|
|
553
575
|
"""
|
|
554
576
|
return self.__file_info
|
|
555
577
|
|
|
@@ -579,8 +601,11 @@ class LocalFile:
|
|
|
579
601
|
If `get_file_path` is called before the file has been downloaded
|
|
580
602
|
"""
|
|
581
603
|
if self.__download_path is None:
|
|
582
|
-
err_msg =
|
|
583
|
-
|
|
604
|
+
err_msg = (
|
|
605
|
+
"File {!r} has no path because it has".format(
|
|
606
|
+
self.name
|
|
607
|
+
),
|
|
608
|
+
"not been downloaded"
|
|
584
609
|
)
|
|
585
610
|
logging.getLogger(__name__).error(err_msg)
|
|
586
611
|
raise RuntimeError(err_msg)
|
|
@@ -607,14 +632,16 @@ class LocalFile:
|
|
|
607
632
|
|
|
608
633
|
def download(self, dest_path, unpack=True):
|
|
609
634
|
"""
|
|
610
|
-
Downloads a file or the contents of a packed file to to the specified
|
|
635
|
+
Downloads a file or the contents of a packed file to to the specified
|
|
636
|
+
`path`.
|
|
611
637
|
|
|
612
638
|
Parameters
|
|
613
639
|
----------
|
|
614
640
|
dest_path:
|
|
615
641
|
Path where the file should be downloaded to.
|
|
616
642
|
unpack:
|
|
617
|
-
Tells the function whether the file should be unpacked to the
|
|
643
|
+
Tells the function whether the file should be unpacked to the
|
|
644
|
+
given folder.
|
|
618
645
|
|
|
619
646
|
Returns
|
|
620
647
|
-------
|
|
@@ -630,7 +657,8 @@ class LocalFile:
|
|
|
630
657
|
logger.debug("Using path {!r}".format(dest_path))
|
|
631
658
|
|
|
632
659
|
if source_file.endswith(".zip") and unpack:
|
|
633
|
-
# Download the zip to a temporary directory and unpack its
|
|
660
|
+
# Download the zip to a temporary directory and unpack its
|
|
661
|
+
# contents to the user dir path
|
|
634
662
|
with TemporaryDirectory() as temp_dir:
|
|
635
663
|
temp_path = os.path.join(temp_dir, source_file)
|
|
636
664
|
self.__download_file(temp_path)
|
{qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/local/executor.py
RENAMED
|
@@ -12,13 +12,15 @@ from qmenta.sdk.local.parse_settings import parse_tool_settings
|
|
|
12
12
|
|
|
13
13
|
def parse_args():
|
|
14
14
|
argp = argparse.ArgumentParser(
|
|
15
|
-
description="Local executor for debugging and testing purposes (SDK
|
|
15
|
+
description="Local executor for debugging and testing purposes (SDK "
|
|
16
|
+
"version {})".format(
|
|
16
17
|
__version__
|
|
17
18
|
)
|
|
18
19
|
)
|
|
19
20
|
argp.add_argument(
|
|
20
21
|
"settings",
|
|
21
|
-
help="JSON file that defines the settings of the tool to be run.
|
|
22
|
+
help="JSON file that defines the settings of the tool to be run."
|
|
23
|
+
"Uses same syntax as in platform. "
|
|
22
24
|
"See qmenta_sdk/examples/",
|
|
23
25
|
)
|
|
24
26
|
argp.add_argument(
|
|
@@ -28,22 +30,26 @@ def parse_args():
|
|
|
28
30
|
argp.add_argument(
|
|
29
31
|
"src_folder",
|
|
30
32
|
help="Source folder where the inputs of the analysis are stored."
|
|
31
|
-
'That is you "get_files" from this folder, that represents an input
|
|
33
|
+
'That is you "get_files" from this folder, that represents an input'
|
|
34
|
+
'container.',
|
|
32
35
|
)
|
|
33
36
|
argp.add_argument(
|
|
34
37
|
"dst_folder",
|
|
35
|
-
help="Destination folder where the outputs of the analysis will be
|
|
38
|
+
help="Destination folder where the outputs of the analysis will be"
|
|
39
|
+
"stored."
|
|
36
40
|
'That is, when you "upload_file", the file is stored in this folder.',
|
|
37
41
|
)
|
|
38
42
|
argp.add_argument(
|
|
39
43
|
"--res-folder",
|
|
40
|
-
help="Folder where the different template files required by the
|
|
44
|
+
help="Folder where the different template files required by the"
|
|
45
|
+
"analysis are stored.",
|
|
41
46
|
default=None,
|
|
42
47
|
)
|
|
43
48
|
argp.add_argument(
|
|
44
49
|
"--tool-path",
|
|
45
50
|
required=True,
|
|
46
|
-
help="Path to the Python tool (package.module:function)
|
|
51
|
+
help="Path to the Python tool (package.module:function)"
|
|
52
|
+
"to be executed",
|
|
47
53
|
)
|
|
48
54
|
argp.add_argument("--log-path", help="Log file")
|
|
49
55
|
argp.add_argument(
|
|
@@ -4,7 +4,8 @@ from contextlib import contextmanager
|
|
|
4
4
|
|
|
5
5
|
class LogCaptureHandler(logging.Handler):
|
|
6
6
|
"""
|
|
7
|
-
Log handler that captures all messages that it handles and exposes them as
|
|
7
|
+
Log handler that captures all messages that it handles and exposes them as
|
|
8
|
+
a list in `log_sequence` attribute.
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
def __init__(self):
|
|
@@ -34,30 +35,35 @@ class GlobFilter(logging.Filter):
|
|
|
34
35
|
@contextmanager
|
|
35
36
|
def capture_log(logger_name_glob, log_level=logging.INFO):
|
|
36
37
|
"""
|
|
37
|
-
Context manager that captures the logs of all loggers that satisfy
|
|
38
|
-
with associated severity greater or equal than
|
|
38
|
+
Context manager that captures the logs of all loggers that satisfy
|
|
39
|
+
`logger_name_glob` with associated severity greater or equal than
|
|
40
|
+
`log_level`.
|
|
39
41
|
|
|
40
42
|
Parameters
|
|
41
43
|
----------
|
|
42
44
|
logger_name_glob : str
|
|
43
45
|
Glob expression that specified the loggers should be captured.
|
|
44
46
|
log_level : int, optional
|
|
45
|
-
Minimum severity level to be captured, included.
|
|
47
|
+
Minimum severity level to be captured, included.
|
|
48
|
+
Default is logging.INFO.
|
|
46
49
|
|
|
47
50
|
Returns
|
|
48
51
|
-------
|
|
49
52
|
LogCaptureHandler
|
|
50
|
-
Handler that exposes the `log_sequence` attribute, which consists of a
|
|
53
|
+
Handler that exposes the `log_sequence` attribute, which consists of a
|
|
54
|
+
list of captured messages.
|
|
51
55
|
|
|
52
56
|
Examples
|
|
53
57
|
--------
|
|
54
58
|
>>> logger = logging.getLogger('package.module.method')
|
|
55
|
-
>>> with capture_log('package.module.*', log_level=logging.WARNING)
|
|
59
|
+
>>> with capture_log('package.module.*', log_level=logging.WARNING)
|
|
60
|
+
as captured_logs:
|
|
56
61
|
... logger.info("This info message won't be captured")
|
|
57
62
|
... logger.warning("This warning message will be captured")
|
|
58
63
|
... logger.critical("This critical message will be also captured")
|
|
59
64
|
... print([record.msg for record in captured_logs.log_sequence])
|
|
60
|
-
['This warning message will be captured', 'This critical message will be
|
|
65
|
+
['This warning message will be captured', 'This critical message will be
|
|
66
|
+
also captured']
|
|
61
67
|
"""
|
|
62
68
|
# Set up the handler, filter and log level
|
|
63
69
|
root_logger = logging.getLogger()
|
{qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/make_entrypoint.py
RENAMED
|
@@ -8,7 +8,8 @@ export PYTHONPATH="${{PYTHONPATH:+$PYTHONPATH:}}{tool_path}"
|
|
|
8
8
|
# ...
|
|
9
9
|
|
|
10
10
|
# Source the export_paths.txt file if exist to load the system paths.
|
|
11
|
-
# This file is created in the ci when performing a multi-level build
|
|
11
|
+
# This file is created in the ci when performing a multi-level build
|
|
12
|
+
# of the docker images
|
|
12
13
|
|
|
13
14
|
if [ -f "/root/export_paths.txt" ]; then
|
|
14
15
|
source /root/export_paths.txt
|
{qmenta_sdk_lib-2.2.dev3493 → qmenta_sdk_lib-2.2.dev3494}/python/qmenta/sdk/tool_maker/inputs.py
RENAMED
|
@@ -209,7 +209,8 @@ class ContainerHandler(Input):
|
|
|
209
209
|
analysis_dir, "input_folder", self.id, folder_to_save_file
|
|
210
210
|
)
|
|
211
211
|
|
|
212
|
-
# This takes into account the path of the file in the input
|
|
212
|
+
# This takes into account the path of the file in the input
|
|
213
|
+
# container in the platform
|
|
213
214
|
logger.info(f"Saving to path: {os.path.dirname(fpath)}.")
|
|
214
215
|
|
|
215
216
|
# in local testing, we need to create the directory
|