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 Code Documentation.