.. raw:: html
:file: ../utils/google_analytics_snippet.html
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:
.. code-block:: python
# 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.
.. code-block:: python
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:
.. code-block:: python
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 :doc:`settings` to learn more about file filters and how to specify the conditions.
.. code-block:: python
# 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.
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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).
.. code-block:: python
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 :doc:`../api_reference`.