File: starters_tutorial.rst

package info (click to toggle)
pyxnat 1.6.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,204 kB
  • sloc: python: 6,016; makefile: 28; sh: 17; xml: 11
file content (377 lines) | stat: -rw-r--r-- 13,475 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
Starters Tutorial
==============================

.. currentmodule:: pyxnat

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 <http://central.xnat.org>`_ is a public XNAT repository managed by the XNAT team
    and updated regularly with the latest improvements of the
    development branch.


Getting started
---------------

Connecting to an 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')

It is also possible to define an :class:`Interface` object without
specifying all the connection settings. In that case :mod:`pyxnat` switches to
interactive mode and prompts the user for the missing information.

>>> central = Interface(server='http://central.xnat.org:8080')
>>> User:my_login
>>> Password:

You can also use a configuration file. The best way to create the file
is to use the ``save_config()`` method on an existing interface.

>>> central.save_config('central.cfg')
>>> central2 = Interface(config='central.cfg')

.. 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.
    You might end up with something like:
    http://server_ip:port/xnat

The main interface class is now divided into logical subinterfaces:
    - data selection
    - general 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
explicit Python objects and methods, or through a ``path`` describing
the data.

Simple requests::

    >>> interface.select.projects().get()
    [..., 'CENTRAL_OASIS_CS', 'CENTRAL_OASIS_LONG', ...]
    >>> interface.select('/projects').get()
    [..., 'CENTRAL_OASIS_CS', '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()
    ['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']

Resource paths
---------------

The resource paths 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.

Absolute paths
~~~~~~~~~~~~~~

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 within the path::

	 /project/IMAGEN/subject/IMAGEN_000055982442
	 EQUALS
	 /projects/IMAGEN/subjects/IMAGEN_000055982442

	 /project/IMAGEN/subject/IMAGEN_000055982442/experiments
	 EQUALS
	 /project/IMAGEN/subjects/IMAGEN_000055982442/experiment


Relative paths and shortcuts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When browsing resources, some levels are often 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
   EQUALS
   /project/IMAGEN/subjects/*/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
important are for creating new resources, deleting
existing ones and testing whether a given resource exists or not::

    >>> my_project = central.select.project('my_project')
    >>> my_project.exists()
    False
    >>> my_project.create()
    >>> my_project.exists()
    True
    >>> subject = my_project.subject('first_subject')
    >>> subject.create()
    >>> subject.delete()
    >>> subject.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()


Additional fields can be configured at the resource creation. It can
be especially useful for datatypes that have some mandatory fields,
and thus would not be created if not specified (this is not a best
practice for XML Schema writers though). It also enables users to set
the resource ID through the REST API instead of just the label (the ID
in this case is generated automatically).

Custom ID example::

    >>> experiment.create(experiments='xnat:mrSessionData',
                          ID='my_custom_ID'
                         )

With additional fields::

    >>> experiment.create(**{'experiments':'xnat:mrSessionData',
                             'ID':'mr_custom_ID',
			     'xnat:mrSessionData/age':'42'}
			  )

.. warning:: When using xpath syntax to declare fields, it is
	mandatory to pass the arguments using a dictionnary because of
	the ``/`` and ``:`` characters. And do not forget to expand
	the dict with the ``**``.

Since you can create different resource levels in a single create call
in pyxnat, it is also possible to configure those levels in a single
call. For example if the subject for that experiment was not created,
you could have specified::

    >>> experiment.create(
		**{'experiments':'xnat:mrSessionData',
                   'ID':'mr_custom_ID',
                   'xnat:mrSessionData/age':'42',
                   'xnat:subjectData/investigator/lastname':'doe',
	           'xnat:subjectData/investigator/firstname':'john',
	           'xnat:subjectData/ID':'subj_custom_ID'
		  })


File support
------------

It is possible to upload and then download files at every REST resource level::

    >>> 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.resource('NIFTI').file('image.nii').size()
    98098
    >>> my_project.resource('NIFTI').file('image.nii').content()
    'T1'
    >>> my_project.resource('NIFTI').file('image.nii').format()
    'NIFTI'
    >>> my_project.resource('NIFTI').file('image.nii').tags()
    'image test'
    >>> my_project.resource('NIFTI').file('image.nii').get()
    '~/.store/nosetests@central.xnat.org/c7a5b961fc504ffc9aa292f76d75fb0c_image.nii'
    >>> my_project.file('image.nii').get_copy()
    '~/.store/nosetests@central.xnat.org/workspace/projects/Volatile/resources/123150742/files/image.nii'
    >>> my_project.file('image.nii').get_copy('/tmp/test.nii')
    '/tmp/test.nii'
    >>> # the resource level can be used to group 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()
    ['NIFTI', 'ANALYZE']
    >>> my_project.resource('ANALYZE').files()
    ['image.hdr', 'image.img']

.. tip::
   New since 0.7, the default ``get()`` method on a file can be given
   a custom path. It will still be handled and tracked by the cache in
   the same way as other files.


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

    >>> # use hard-coded shortcuts from the REST API
    >>> my_project.attrs.set('secondary_ID', 'myproject')
    >>> my_project.attrs.get('secondary_ID')
    'myproject'
    >>> # use XPATH from standard or custom XNAT Schema
    >>> my_project.attrs.set('xnat:projectData/keywords', 'test project')
    >>> my_project.attrs.get('xnat:projectData/keywords')
    'test project'
    >>> # get or set multiple attributes in a single request to improve performance
    >>> my_project.attrs.mset({'xnat:projectData/keywords':'test project', 'secondary_ID':'myproject'})
    >>> my_project.attrs.mget(['xnat:projectData/keywords', 'secondary_ID'])
    ['test porject', 'myproject']




The search engine
------------------

The XNAT search engine can be queried via the REST model. It can be
used to retrieve a specific subset of REST resources or a table
containing the relevant values. The following queries find all the
subjects that are within `my_project` older than 14::

    >>> constraints = [('xnat:subjectData/SUBJECT_ID','LIKE','%'),
                      ('xnat:subjectData/PROJECT', '=', 'my_project'),
                      'OR',
                      [('xnat:subjectData/AGE','>','14'),
                       'AND'
                       ]
                      ]
    >>> # retrieve experiments
    >>> interface.select('//experiments').where(constraints)
    >>> # retrieve table with one subject per row and the columns SUBJECT_ID and AGE
    >>> interface.select('xnat:subjectData', ['xnat:subjectData/SUBJECT_ID', 'xnat:subjectData/AGE']).where(constraints)

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

To get the searchable types and fields to put in the constraints, rows
and columns parameters, use the ``Interface.inspect.datatypes``
method::

    >>> central.inspect.datatypes(optional_filter)
    [..., 'xnat:subjectData', 'xnat:projectData', 'xnat:mrSessionData',  ...]
    >>> central.inspect.datatypes('xnat:subjectData', optional_filter)
    ['xnat:subjectData/SUBJECT_ID',
     'xnat:subjectData/INSERT_DATE',
     'xnat:subjectData/INSERT_USER',
     'xnat:subjectData/GENDER_TEXT',
     ...]


.. tip::
   How to get all the results in a query?
       >>> interface.select('xnat:subjectData',
       	   			['xnat:subjectData/SUBJECT_ID',
       		                 'xnat:subjectData/AGE']).all()

.. tip::
   How to get all the columns from a datatype?
       >>> table = interface.select('xnat:subjectData').where(...)

.. tip::
   Then to get everything:
   	>>> table = interface.select('xnat:subjectData').all()