.. 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`.