Quick start

This is a short tutorial going through the main features of this API. Depending on the policy of the XNAT server you are using, and your user level, you can have read and write access to a specific set of resources. During this tutorial, we will use a fake standard user account on the XNAT central repository, where you will have limited access to a number of projects and full access to the projects you own.

Note

XNAT Central is a public XNAT repository managed by the XNAT team and updated regularly with the latest improvements of the development branch.


[1]http://central.xnat.org

Overview

Connecting to a XNAT server requires valid credentials so you might want to start by requesting those on the Web interface of your server.

>>> from pyxnat import Interface
>>> central = Interface(server='http://central.xnat.org:8080',
                        user='my_login',
                        password='my_pass',
                        cachedir=os.path.join(os.path.expanduser('~'), '.store')
                        )

The cachedir argument specifies where the local disk-cache will be stored. Every query and downloaded file will be stored here so choose a location with sufficient free space. If the datastore argument if not given, a temporary location is picked automatically but it may be flushed at every reboot of the machine. Here the cachedir argument is set to a .store directory in the user’s home directory in a cross platform manner. If the cachedir starts getting full, a warning will be printed in the stdout. At the moment no garbage collection of the cache is enabled.

Warning

Depending on the server configuration, you may have to include the port in the server url, as well as the name of the XNAT tomcat application. So you might end up with something like: http://server_ip:port/xnat

The main interface class is now divided into logical subinterfaces:
  • data selection
  • search management
  • users management
  • cache management
  • server instrospection

Data selection

Now that we have an Interface object, we can start browsing the server with the select`subinterface which can be used, either with expicit Python objects and methods, or through a `path describing the data.

Simple requests:

>>> interface.select.projects().get()
[..., u'CENTRAL_OASIS_CS', u'CENTRAL_OASIS_LONG', ...]
>>> interface.select('/projects').get()
[..., u'CENTRAL_OASIS_CS', u'CENTRAL_OASIS_LONG', ...]

Nested requests:

>>> interface.select.projects().subjects().get()
>>> interface.select('/projects/*/subjects').get()
>>> interface.select('/projects/subjects').get()
>>> interface.select('//subjects').get()
['IMAGEN_000000001274', 'IMAGEN_000000075717', ...,'IMAGEN_000099954902']

Filtered requests:

>>> interface.select.projects('*OASIS_CS*').get()
>>> interface.select('/projects/*OASIS_CS*').get()
[u'CENTRAL_OASIS_CS']

>>> interface.select.project('IMAGEN').subjects('*55*42*').get()
>>> interface.select('/projects/IMAGEN/subjects/*55*42*').get()
['IMAGEN_000055203542', 'IMAGEN_000055982442', 'IMAGEN_000097555742']

REST path

The REST path that can be passed as an argument to select is a powerful tool but can easily generate thousands of queries so one has to be careful when using it.

A full path to a resource is a sequence of resource level and resource_id pairs:

/project/IMAGEN/subject/IMAGEN_000055982442

A full path to a resource listing is a sequence of resource level and resource_id pairs finishing by a plural resource level (i.e. with an ‘s’):

/project/IMAGEN/subject/IMAGEN_000055982442/experiments

The first nice thing here is that you actually don’t have to worry about resource level to be plural or singular.

Correct path   works as well
/project/IMAGEN/subject/IMAGEN_000055982442 = /projects/IMAGEN/subjects/IMAGEN_000055982442
/project/IMAGEN/subject/IMAGEN_000055982442/experiments = /project/IMAGEN/subjects/IMAGEN_000055982442/experiment

Often, when browsing resources, some levels will left without any IDs and filled with * filters instead, which leads to paths like:

/projects/*/subjects/*/experiments/*

That can instead be written:

/projects/subjects/experiments OR //experiments

To have all the experiments from a specific project:

/project/IMAGEN//experiments

The double slash syntax can be used anywhere in the path and any number of time:

//subjects//assessors EQUALS /projects/*/subjects/*/experiments/*/assessors/*

Sometimes, a path will generate more than one path because it can be interpreted in different way:

//subjects//assessors//files

Generates:

/projects/*/subjects/*/experiments/*/assessors/*/in_resources/*/files/*
/projects/*/subjects/*/experiments/*/assessors/*/out_resources/*/files/*
/projects/*/subjects/*/experiments/*/assessors/*/resources/*/files/*

Warning

If you try //files, it will generate all the possible descendant paths:

/projects//subjects//experiments//resources//files/*
/projects//subjects//experiments//reconstructions//in_resources//files/
/projects//subjects//experiments//scans//resources//files/
/projects//subjects//experiments//assessors//out_resources//files/
/projects//subjects//resources//files/
/projects//resources//files/*
/projects//subjects//experiments//reconstructions//out_resources//files/
/projects//subjects//experiments//assessors//in_resources//files/
/projects//subjects//experiments//assessors//resources//files/

If the server has decent amount a data it will take ages to go through all the resources.

Resources operations

Several operations are accessible for every resource level. The most importants are responsible for creating new resources, deleting existing one and testing whether a given resource exists or not:

>>> my_project = interface.select.project('my_project')
>>> my_project.exists()
False
>>> my_project.create()
>>> my_project.exists()
True
>>> subject_1 = my_project.subject('first_subject')
>>> subject_1.create()
>>> subject_1.delete()
>>> subject_1.exists()
False

An optional keyword argument is available to specify the datatype from the XNAT schema. The keyword must match the name of the REST level.

>>> subject.create()
>>> subject.experiment('pet_session').create(experiments='xnat:petSessionData')

It is also possible to create resources without having to create the parent resources first. For example:

>>> central.select('/project/PROJECT/subject/SUBJECT').exists()
False
>>> central.select('/project/PROJECT/subject/SUBJECT/experiment/EXP').exists()
False
Specifiy the datatype on multiple levels::
>>> central.select('/project/PROJECT/subject/SUBJECT/experiment/EXP/scan/SCAN'
                  ).create(experiments='xnat:mrSessionData', scans='xnat:mrScanData')
Use default datatypes::
>>> central.select('/project/PROJECT/subject/SUBJECT/experiment/EXP/scan/SCAN'
                  ).create()

Warning

Sometimes a datatype requires some fields to be filled at the creation of the resource. This is not possible by REST at the moment so any such type will have to be created via an xml document and uploaded to the XNAT server. The XML uploading is not currently supported by pyxnat but will be.

File support

It is possible to upload and then download files at every resource level. When grouping files makes sense, it is possible to use a level called resources:

>>> my_project.files()
[]
>>> my_project.file('image.nii').put('/tmp/image.nii')
>>> # you can add any of the following arguments to give additional information
      on the file you are uploading
>>> my_project.file('image.nii').put( '/tmp/image.nii',
                                      content='T1',
                                      format='NIFTI'
                                      tags='image test'
                                    )
>>> my_project.file('image.nii').size()
98098
>>> my_project.file('image.nii').content()
'T1'
>>> my_project.file('image.nii').format()
'NIFTI'
>>> my_project.file('image.nii').tags()
'image test'
>>> my_project.file('image.nii').get()
<open file '<fdopen>', mode 'r' at 0x9602750>
>>> my_project.file('image.nii').get_copy()
'~/XnatStore/REST/projects/my_project/resources/123150742/files/image.nii'
>>> my_project.file('image.nii').get_copy('/tmp')
'/tmp/image.nii'
>>> # grouping files
>>> my_project.resource('ANALYZE').file('image.hdr').put('/tmp/image.hdr')
>>> my_project.resource('ANALYZE').file('image.img').put('/tmp/image.img')
>>> my_project.resources()
['123150742', 'ANALYZE']
>>> my_project.resource('ANALYZE').files()
['image.hdr', 'image.img']
>>> my_project.resource('ANALYZE').file('image.hdr').get()
'~/XnatStore/REST/projects/my_project/resources/ANALYZE/files/image.hdr'
>>> my_project.resource('ANALYZE').file('image.img').get()
'~/XnatStore/REST/projects/my_project/resources/ANALYZE/files/image.img'

Note

For the image.nii, the resources level is implicit and an ID for that level is generated automatically.

Metadata support

Each resource level also has a set of metadata fields that can be informed. This set of fields depends on the resource level and on its type in the XNAT schema:

>>> my_project.attrib.keys()
['note','alias','secondary_ID','name','pi_lastname',
 'label','keywords','pi_firstname','ID','description']
>>> my_project.attrib.set('note', 'a note')
>>> my_project.attrib.get('note')
'a note'
>>> my_project.attrib.update({'pi_lastname':'doe', 'pi_firstname':'john'})
>>> my_project.attrib['description'] = 'a description here'
>>> my_project.attrib.set('description', 'a description here')

Warning

Write access is currently broken.

See the Attrib class reference documentation for further details.

Note

The list of fields is actually generated from a config file so one can set a convenient shortcut on the XNAT schema.

e.g. Choose your shortcut by editing the configuration file:
  • description = xnat:projectData/description
  • desc = xnat:projectData/description

The configuration file is called xnat_shortcuts.cfg and is located in $HOME/.pyxnat under Unix and in $HOME/pyxnat under Windows.

Warning

The list of fields is not generated knowing the type of the resource in the XNAT schema.

It means that an experiment will have both:
  • tracer_isotope = xnat:petSessionData/tracer/isotope/half-life
  • coil = xnat:mrSessionData/coil

But if the experiment it a mrSessionData, it will not be able to set tracer_isotope and tracer_isotope value will always be None.


[2]http://nrg.wikispaces.com/XNAT+REST+XML+Path+Shortcuts

Make complex searches

The XNAT search engine can be queried via the REST model. The following query finds all the subjects that are within my_project or that have an age superior to 14:

>>> search = interface.search( 'my_search',
                               [ ('xnat:subjectData/SUBJECT_ID','LIKE','%'),
                                 ('xnat:subjectData/PROJECT', '=', 'my_project'),
                                 'OR',
                                 [ ('xnat:subjectData/AGE','>','14'),
                                   'AND'
                                 ]
                               ]
                             )
>>> search.get_subjects()

See the Search and SeachManager classes reference documentation for further details.

Table Of Contents

Previous topic

The underlying model

Next topic

Reference documentation

This Page