Step 5 of 5: Command, Action, ACE Practice
Goal
In this step, you will configure a new Command, Action, ACE set to act on XNAT session data.
Make a new Command, Action, and ACE
In this step, we'll use the custom Python image we generated in Part 1 - Step 4 labeled xnat/dcmstack_python:v1. Recall that we loaded a base python image with several toolboxes and a python script, pydicom.py
.
pydicom.py
# /bin/env python
# Converts a directory of one or more DICOM files into the NIfTI format
# Primary use is to test the XNAT Docker service.
# Input directory is assumed to be /input
# Input directory format is assumed to be /data/input/*.dcm
# Output directory is assumed to be /data/output
# Output directory format will be generated as /data/output/data.nii.gz
# USAGE: python pyscript.py
import sys
import os
import dcmstack
import getopt
from glob import glob
def main(argv):
input_dir = '/input'
output_dir = '/output'
dcm = glob(input_dir + os.path.sep + '*.dcm')
nifit_file_name = output_dir + os.path.sep + 'scan' + '.nii.gz';
dcm_to_nii(dcm, nifit_file_name)
def dcm_to_nii(dcm, nii_file_name):
try:
stacks = dcmstack.parse_and_stack(dcm)
for stack in stacks.itervalues():
nii = stack.to_nifti()
nii.to_filename(nii_file_name)
break
except:
print 'dcmstack DICOM to NIFTI failed.'
if __name__ == "__main__":
main(sys.argv[1:])
When run within our xnat/dcmstack_python:v1 container, this script takes any .dcm files it finds in /input and converts them to NIfII format in the /output folder. The following setup will build a context for that script, providing XNAT image data at the expected location within our Docker container, and execute pydicom.py to process the provided data.
Register The Image With XNAT
Register the image with XNAT and get the id.
- Return to the XNAT Swagger page.
- Expand
docker-images-rest-api
, andPOST /xapi/docker/images
. - Enter JSON in the
dockerImageDto
field:{"name":"xnat/dcmstack_python","image-id":"sha256:041fa8c8bacd2f1106f2b98fb41744c6cb98e303e2f61ce8f1958faed22e22fd"}
. - Press the "Try it out!" button.
- Note the id in the Response Body. We will use it in a moment.
Initialize A Command For This Image In XNAT
- Expand
command-rest-api
, andPOST /xapi/commands
. Enter the following JSON, using the Image id that you just got as the
docker-image.id
value.CODE{ "name": "dicom-scan", "description": "run a script on a scan's dicom files from an input mount", "docker-image": {"id": 2}, "mounts-in": [ { "name": "DICOM", "path": "/input" } ], "mounts-out": [ { "name": "NIFTI", "path": "/output" } ], "run-template": ["python", "/scripts/pydicom.py"], "type": "docker-image" }
- Press the "Try it out!" button.
Note the id in the Response Body. We will use it in a moment.
Command mounts
Let us pause to study this Command. It has two new properties that we have not seen before:
mounts-in
andmounts-out
. The former is where input files will be made available to your container, and the latter is where you can write output files for XNAT to read when your container execution finishes. The names we gave are, by default, the names of resources that XNAT will look for on whatever object we use to resolve the ACE. We will see more about this in a few moments when we define an Action and resolve an ACE from it.
Create An XNAT Action That Can Run The Command
- Expand
action-rest-api
, andPOST /xapi/actions
. Enter the following JSON in the
actionDto
field, using the Command id that you just got as thecommand-id
value.CODE{ "name":"dicom-scan", "command-id": 3, "root-xsi-type": "xnat:imageScanData" }
Note that this command will run on a scan, which does not have a globally unique id. The refer to a scan, we will use an id which is
<session id>:<scan id>
.- Press the "Try it out!" button.
Ensure That The Action Can Run In Your XNAT Context
- Expand
ace-rest-api
, andGET /xapi/aces
. - Enter
XNAT_E00001:301
as theid
, andxnat:imageScanData
as thexsiType
. Press the "Try it out!" button.
Example response body:CODE[ { "name": "dicom-scan", "description": null, "inputs": [], "action-id": 2, "command-id": 3, "project": "Day3Data", "root-id": "XNAT_E00001:301", "resources-staged": [ { "path": "/data/xnat/archive/Day3Data/arc001/5Yp0E/SCANS/301/DICOM", "overwrite": false, "name": "DICOM", "mount": "DICOM" } ], "resources-created": [ { "path": null, "overwrite": false, "name": "NIFTI", "mount": "NIFTI" } ] } ]
Run Your Action in XNAT (Which Performs The Command On Your Image)
- Copy the ACE object from the Response Body (do not include the surrounding square brackets
[]
which denote a list). - Expand
POST /xapi/aces
. Paste the ACE that you just copied into the
aceDto
field.CODE{ "name": "dicom-scan", "description": null, "inputs": [], "action-id": 2, "command-id": 3, "project": "Day3Data", "root-id": "XNAT_E00001:301", "resources-staged": [ { "path": "/data/xnat/archive/Day3Data/arc001/5Yp0E/SCANS/301/DICOM", "overwrite": false, "name": "DICOM", "mount": "DICOM" } ], "resources-created": [ { "path": null, "overwrite": false, "name": "NIFTI", "mount": "NIFTI" } ] }
- Press the "Try it out!" button.
Example response:
{
"name": "dicom-scan",
"description": null,
"inputs": [],
"timestamp": 1465176871666,
"enabled": true,
"created": 1465176871434,
"id": 2,
"disabled": 0,
"action-id": 2,
"project": "Day3Data",
"root-id": "XNAT_E00001:301",
"resources-staged": [
{
"path": "/data/xnat/archive/Day3Data/arc001/5Yp0E/SCANS/301/DICOM",
"overwrite": false,
"name": "DICOM",
"mount": "DICOM"
}
],
"resources-created": [
{
"path": null,
"overwrite": false,
"name": "NIFTI",
"mount": "NIFTI"
}
],
"resolved-command": {
"command-id": 3,
"run": [
"python",
"/scripts/pydicom.py"
],
"docker-image-id": "sha256:041fa8c8bacd2f1106f2b98fb41744c6cb98e303e2f61ce8f1958faed22e22fd",
"env": {},
"mounts-in": [
{
"name": "DICOM",
"local-path": "/data/xnat/archive/Day3Data/arc001/5Yp0E/SCANS/301/DICOM",
"remote-path": "/input",
"read-only": true
}
],
"mounts-out": [
{
"name": "NIFTI",
"local-path": "/data/xnat/build/6d18d6f6-3212-4e32-8801-895c952b35f3",
"remote-path": "/output",
"read-only": false
}
]
},
"container-id": "56e2eba875c28121b8a2181e2897a04300ed5ea0b78b7452aa91c7293e1d1e05"
}
Let's inspect this output. You can see in resolved-command.mounts-out.local-path
that a random path was created in XNAT's build space for this ACE execution to write its output data. XNAT will (eventually...) be able to upload any files created in that directory back to the root object as a new resource. But that doesn't happen just yet. For now, check that that directory for your newly generated NIfTI file.
Completed!
While the Action/Context Execution infrastructure is still in early stages of development, we hope this gives you a flavor of XNAT functionality on the horizon.
Feel free to experiment with other interesting Docker images you've found or created.