Tool development with the SDK
When a tool is executed in the platform, a python script inside the container is called. By default, this script can be found at /root/tool.py, but you can move it or rename it provided you specify it later in the platform.
The tool script receives a parameter which provides an interface between the container and the platform to download the input files, upload the results and other functionalities:
# The script receives an instance of AnalysisContext to interface with the QMENTA platform
def run(context):
# Get information from the platform such as patient name, user_id, ssid...
analysis_data = context.fetch_analysis_data()
file_handlers = context.get_files('input')
path = file_handler[0].download('/root/data/') # Download the first file
context.set_progress(value=0, message="Going to work...") # Report some feedback
result_path = do_something(path) # Process data
context.upload_file(result_path, 'result.extension') # Send the results
context.set_progress(value=100, message="Complete!") # Report some feedback
Before going into the details of those functions, some basic definitions must be considered:
The input files are downloaded to the tools at run-time. The same happens with the upload of the results; the tool has to upload the results of the analysis in order to visualize them with the QMENTA platform.
An analysis has one (default, named input) or more inputs encapsulated into input containers which can represent any selection of data files. For instance, one could select files from different time-points for one single patient as two input containers with different IDs.
Optionally, files can optionally have tags, modality or metadata (file_info). This information can be used by the developer/platform to better handle its processing and visualisation.
When the python script (by default, tool.py) ends its execution, the analysis will conclude and the container will be stopped.
Downloading the input files
When the QMENTA platform starts an analysis, the user selects which data will be used for each input container. These files can be downloaded as desired inside the container using three distinct types of optional filters. By modality, by tags or by file name (using a matching regular expression). This typically involves two steps: first, getting the list of desired file handlers, and then downloading them to a specific path or folder.
context.get_files(
'input', # Name of the input container
file_filter_condition_name = 'c_my_selection', # Optional condition id of the tool settings
modality = 'T1', # Optional modality string
tags = {'mask', 'strip'}, # Optional set of tags
reg_expression = '*.nii.gz' # Optional reg exp string
)
Example:
def run(context):
t1_files = context.get_files('input', modality='T1') # Get all file handlers with modality T1
t1_path = t1_files[0].download('/root/files/') # Download the first one to the directory
# Do something with t1_path
The recommended way to get the input files, is to use the name of a file filter to get only the files that meet the conditions specified in the tool specification file. See Parameters and Input Files to learn more about file filters and how to specify the conditions.
# Get all files that passed c_TEST (including any selection the user may have done using the GUI)
context.get_files('input', file_filter_condition_name='c_TEST')
# Get all files that passed c_TEST and have no tags
context.get_files('input', file_fitler_condition_name='c_TEST', tags=set()
# Get all files that passed c_TEST and have the DTI tag in their list of tags
context.get_files('input', file_fitler_condition_name='c_TEST', tags={'DTI'})
# Get all files that passed c_TEST whose name match the given regexp
context.get_files('input', file_filter_condition_name='c_TEST', reg_expression='^some_exp[0-9]')
# If you want to get all the files (regardless any user decision between multiple valid files) use:
context.get_files('input')
Downloading tool resources
Optionally, tools can download shared resource files from the platform. These files are uploaded using the Tool Resources manager under the My Data tab. The purpose of having tool resources is to avoid having to include files that are used by multiple tools in their Docker images.
Note
This feature can be especially helpful to handle files that are only needed by a tool under certain circumstances. For instance, depending on the age of the patient or the value of an analysis setting we may want to download template files specifically for such cases.
If you wish to know more about this feature, please contact us at dev@qmenta.com.
context.download_resource(
resource_path, # Path of the file in the platform's resources manager
destination_path, # Path where the file is going to be downloaded
)
Example:
def run(context):
context.download_resource('a/b/c/file.nii.gz', '/root/file.nii.gz')
Processing
After downloading the input files to the container, the script is ready to call your programs or any third party tool that is required. The run method variable allows the tool to report back to the platform using a number (between 0 and 100) and/or a message.
Example:
def run(context):
context.set_progress(value = 50) # Show a only progress bar
context.set_progress(message = "Computing X...") # Show only a message
context.set_progress(value = 100, message = "Finished") # Show both a message and a progress bar
Working with metadata
Tools can edit the metadata of the session they are running on within the scope of a project. The SDK has two simple functions to set (set_metadata_value(key, value)) and get (get_metadata_value(key)) their values at run time:
Example:
biomarker_value = 12345.67 # Can be int, str, float or a list of the former ones
context.set_metadata_value(
'biomarker_a', # The id (key) of metadata parameter
biomarker_value, # Its new or updated value
title='Biomarker A', # Optional title for the metadata parameter
readonly=True # Do not allow users to edit the value on the platform
)
value = context.get_metadata_value(
'biomarker_a' # The id (key) of metadata parameter
)
When no title is provided to the set_metadata_value function, the id (key) will be used on the metadata table as the title of the parameter.
Warning
For security reasons, once the title and the readonly properties are set for the first time, they will stay that way regardless of future calls with different settings.
Note
Remember that you can always export/import metadata using the platform, or filter the sessions by specifying arbitrarily complex criteria on the metadata values.
Uploading the results
Similarly to the download, the output data of the tool can be uploaded to the platform at any time during the execution of an analysis as follows:
context.upload_file(
'/path/to/file/file.zip', # Path in the container (origin)
'file.zip', # Path in the output container (destination)
modality = 'T1', # Optional modality string
tags = {'mask', 'strip'}, # Optional set of tags
file_info = {'param_1': 123, 'text': 'abc'}, # Optional field with file metadata
file_format = "dicom" # Optional field used to identify "dicom" or "nifti" files
)
Note
The file_format argument should only be used to identify a .ZIP file containing .DCM slices for a single DICOM series as such.
Tip
If you upload a file named report.pdf, users will be able to automatically download it by clicking the button Get Report in the Results menu in the top bar of the My Analyses section.
Example:
def run(context):
context.upload_file('./stripped_T1.nii.gz', 'stripped_T1.nii.gz', modality='T1', tags={'strip'})
The output container path can also include directories (which will be created automatically).
def run(context):
context.upload_file('./stripped_T1.nii.gz', 'A/B/C/stripped_T1.nii.gz')
For more information on the available functions, see the API Reference.