annotate Sphinx/source/plugins/python.rst @ 696:29e49f03dc27

fix
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 10 Jun 2021 18:13:58 +0200
parents f4fc12ed00ee
children 7581dc208323
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
343
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
1 .. _python-plugin:
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
2
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
3
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
4 Python plugin for Orthanc
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
5 =========================
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
6
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
7 .. contents::
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
8
371
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
9
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
10 Overview
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
11 --------
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
12
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
13 This plugin can be used to write :ref:`Orthanc plugins
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
14 <creating-plugins>` using the `Python programming language
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
15 <https://en.wikipedia.org/wiki/Python_(programming_language)>`__
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
16 instead of the more complex C/C++ programming languages.
343
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
17
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
18 Python plugins have access to more features and a more consistent SDK
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
19 than :ref:`Lua scripts <lua>`. The Python API is automatically
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
20 generated from the `Orthanc plugin SDK in C
678
17c1ff4e6ae4 Orthanc 1.9.3
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 642
diff changeset
21 <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.3/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
22 using the `Clang <https://en.wikipedia.org/wiki/Clang>`__ compiler
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
23 front-end.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
24
462
fcc352fd4283 upgrade python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 442
diff changeset
25 As of release 2.0 of the plugin, the coverage of the C SDK is about
fcc352fd4283 upgrade python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 442
diff changeset
26 75% (119 functions are automatically wrapped in Python out of a total
fcc352fd4283 upgrade python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 442
diff changeset
27 of 157 functions in the Orthanc SDK 1.7.2).
343
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
28
371
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
29
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
30 Source code
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
31 -----------
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
32
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
33 * Link to the `official releases of this plugin
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
34 <https://www.orthanc-server.com/browse.php?path=/plugin-python>`__.
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
35
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
36 * Link to the `code repository
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
37 <https://hg.orthanc-server.com/orthanc-python/>`__.
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
38
5cb37e6b014b explicit links to the source code of python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
39
353
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
40 Licensing
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
41 ---------
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
42
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
43 Pay attention to the fact that this plugin is licensed under the terms
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
44 of the `AGPL license
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
45 <https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License>`__.
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
46
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
47 This has an important consequence: If you distribute Orthanc to
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
48 clients together with one Python script, or if you put an Orthanc
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
49 server equipped with one Python script on a Web portal, you **must**
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
50 disclose the source code of your Python script to the Orthanc
354
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
51 community under the terms of the AGPL license.
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
52
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
53 We suggest you to put the source code of your Python scripts on the
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
54 dedicated `"OrthancContributed" repository on GitHub
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
55 <https://github.com/jodogne/OrthancContributed/tree/master/Plugins>`__,
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
56 and/or to send it to the `Orthanc Users
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
57 <https://groups.google.com/forum/#!forum/orthanc-users>`__ discussion
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 353
diff changeset
58 group.
353
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
59
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
60 Check out the :ref:`FAQ about licensing <licensing>` for more context.
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
61
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
62
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
63 Usage
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
64 -----
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
65
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
66 Docker
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
67 ......
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
68
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
69 .. highlight:: json
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
70
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
71 The most direct way of starting Orthanc together with the Python
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
72 plugin is through :ref:`Docker <docker>`. Let's create the file
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
73 ``/tmp/hello.py`` that contains the following basic Python script::
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
74
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
75 print('Hello world!')
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
76
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
77 .. highlight:: json
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
78
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
79 Let's also create the file ``/tmp/orthanc.json`` that contains the
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
80 following minimal :ref:`configuration for Orthanc <configuration>`::
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
81
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
82 {
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
83 "StorageDirectory" : "/var/lib/orthanc/db",
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
84 "RemoteAccessAllowed" : true,
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
85 "Plugins" : [
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
86 "/usr/local/share/orthanc/plugins"
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
87 ],
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
88 "PythonScript" : "/etc/orthanc/hello.py"
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
89 }
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
90
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
91 .. highlight:: bash
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
92
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
93 Given these two files, Orthanc can be started as follows::
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
94
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
95 $ docker run -p 4242:4242 -p 8042:8042 --rm \
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
96 -v /tmp/orthanc.json:/etc/orthanc/orthanc.json:ro \
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
97 -v /tmp/hello.py:/etc/orthanc/hello.py:ro \
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
98 jodogne/orthanc-python
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
99
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
100 .. highlight:: text
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
101
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
102 You'll see the following excerpt in the log, which indicates that the Python plugin is properly loaded::
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
103
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
104 W0331 15:48:12.990661 PluginsManager.cpp:269] Registering plugin 'python' (version mainline)
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
105 W0331 15:48:12.990691 PluginsManager.cpp:168] Python plugin is initializing
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
106 W0331 15:48:12.990743 PluginsManager.cpp:168] Using Python script "hello.py" from directory: /etc/orthanc
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
107 W0331 15:48:12.990819 PluginsManager.cpp:168] Program name: /usr/local/sbin/Orthanc
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
108 Hello world!
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
109
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
110
405
ba486cac480a link to python sample
Alain Mazy <alain@mazy.be>
parents: 379
diff changeset
111 `Here <https://bitbucket.org/osimis/orthanc-setup-samples/src/master/docker/python/>`__ is a full example
ba486cac480a link to python sample
Alain Mazy <alain@mazy.be>
parents: 379
diff changeset
112 of a more complex setup using the :ref:`osimis/orthanc <docker-osimis>` images.
ba486cac480a link to python sample
Alain Mazy <alain@mazy.be>
parents: 379
diff changeset
113
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
114 Compiling from source
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
115 .....................
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
116
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
117 .. highlight:: bash
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
118
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
119 The procedure to compile this plugin from source is similar to that
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
120 for the :ref:`core of Orthanc <compiling>`. The following commands
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
121 should work for most UNIX-like distribution (including GNU/Linux)::
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
122
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
123 $ mkdir Build
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
124 $ cd Build
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
125 $ cmake .. -DPYTHON_VERSION=3.7 -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
126 $ make
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
127
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
128 Before running CMake, make sure that the Python interpreter and its
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
129 associated development library are installed. On Ubuntu 18.04 LTS, you
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
130 would for instance install packages ``libpython3.7-dev`` and
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
131 ``python3.7``.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
132
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
133 The compilation will produce the shared library ``OrthancPython``,
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
134 that can be loaded by properly setting the ``Plugins``
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
135 :ref:`configuration option <configuration>` of Orthanc.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
136
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
137 **Warning:** The shared library is only compatible with the Python
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
138 interpreter whose version corresponds to the value of the
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
139 ``PYTHON_VERSION`` argument that was given to CMake.
501
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
140
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
141 **Note for OS X:** As indicated by `Stephen Douglas Scotti
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
142 <https://groups.google.com/g/orthanc-users/c/RnmZKFv8FaY/m/HhvOD2A2CAAJ>`__,
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
143 here is a sample invocation of CMake to force the version of Python to
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
144 be used on OS X::
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
145
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
146 $ cmake .. -DPYTHON_VERSION=3.8 -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release \
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
147 -DPYTHON_LIBRARY=/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/libpython3.8.dylib \
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
148 -DPYTHON_INCLUDE_DIR=/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/include/python3.8/
98592b9e7ad5 force version of python on os x
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 490
diff changeset
149
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
150
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
151 Microsoft Windows
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
152 .................
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
153
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
154 Pre-compiled binaries for Microsoft Windows `are also available
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
155 <https://www.orthanc-server.com/browse.php?path=/plugin-python>`__.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
156
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
157 Beware that one version of the Python plugin can only be run against
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
158 one version of the Python interpreter. This version is clearly
442
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
159 indicated in the filename of the precompiled binaries.
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
160
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
161 .. highlight:: text
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
162
442
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
163 You are of course free to compile the plugin from sources. You'll have
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
164 to explicitly specify the path to your Python installation while
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
165 invoking CMake. For instance::
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
166
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
167 C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=2.7 -DPYTHON_WINDOWS_ROOT=C:/Python27 \
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
168 -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 15 2017"
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
169
442
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
170 **Note about debug builds**: Usually, building Python modules such as the Python
377
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
171 plugin for Orthanc in debug mode (where ``_DEBUG`` is defined) leads to a module
442
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
172 (``.exe`` or ``.dll``) that requires a debug build of Python, and debug versions of all
377
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
173 the Python libraries. This is quite cumbersome, for it requires building Python
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
174 on your own or downloading additional debug files.
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
175
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
176 Since using a debug build of Python is only necessary in very specific cases
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
177 (such as the debugging of code at the boundary between Python and an extension),
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
178 we have changed the default behavior to use the release Python library by default.
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
179
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
180 This means that you are able to build this plugin in debug mode with the
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
181 standard Python distribution.
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
182
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
183 In case you need to use the Python debug libraries, you can instruct the build
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
184 system to do so by setting the ``PYTHON_WINDOWS_USE_RELEASE_LIBS`` CMake option,
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
185 that is ``ON`` by default, to ``OFF``. The previous build example would then be,
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
186 should you require a full debug build::
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
187
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
188 C:\orthanc-python\Build> cmake .. -DPYTHON_VERSION=2.7 -DPYTHON_WINDOWS_ROOT=C:/Python27 \
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
189 -DSTATIC_BUILD=ON -DPYTHON_WINDOWS_USE_RELEASE_LIBS=OFF \
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
190 -DCMAKE_BUILD_TYPE=Debug -G "Visual Studio 15 2017"
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
191
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
192 Please note that this CMake option only impacts **debug** builds under Windows,
ffe62e6c086f Added a note about the debug libraries when building under Windows with Visual Studio.
Benjamin Golinvaux <bgo@osimis.io>
parents: 375
diff changeset
193 when using (any version of) the Microsoft Visual Studio compiler.
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
194
442
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
195 The precompiled binaries all use release (i.e. non-debug) versions of Python.
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
196
8b2c648c0f46 clarifications about windows precompiled binaries
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 405
diff changeset
197
364
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
198 Configuration options
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
199 ---------------------
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
200
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
201 The only two configuration options that are available for this plugin
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
202 are the following:
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
203
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
204 * ``PythonScript`` indicates where the Python script is located. If
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
205 this configuration option is not provided, the Python plugin is not
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
206 started.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
207
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
208 * ``PythonVerbose`` is a Boolean value to make the Python interpreter
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
209 verbose.
234de55ed125 usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 354
diff changeset
210
343
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
211
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
212 Samples
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
213 -------
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
214
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
215 Extending the REST API
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
216 ......................
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
217
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
218 .. highlight:: python
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
219
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
220 Here is a basic Python script that registers two new routes in the
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
221 REST API::
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
222
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
223 import orthanc
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
224 import pprint
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
225
343
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
226 def OnRest(output, uri, **request):
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
227 pprint.pprint(request)
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
228 print('Accessing uri: %s' % uri)
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
229 output.AnswerBuffer('ok\n', 'text/plain')
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
230
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
231 orthanc.RegisterRestCallback('/(to)(t)o', OnRest)
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
232 orthanc.RegisterRestCallback('/tata', OnRest)
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
233
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
234 .. highlight:: json
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
235
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
236 Here is the associated minimal configuration file for Orthanc
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
237 (provided the Python script is saved as ``rest.py``)::
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
238
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
239 {
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
240 "Plugins" : [ "." ],
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
241 "PythonScript" : "rest.py",
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
242 "PythonVerbose" : false
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
243 }
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
244
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
245 .. highlight:: bash
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
246
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
247 The route can then be accessed as::
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
248
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
249 $ curl http://localhost:8042/toto
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
250 ok
fff45618262d creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff changeset
251
469
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
252
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
253 .. _python-changes:
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
254
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
255 Listening to changes
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
256 ....................
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
257
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
258 .. highlight:: python
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
259
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
260 This sample uploads a DICOM file as soon as Orthanc is started::
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
261
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
262 import orthanc
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
263
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
264 def OnChange(changeType, level, resource):
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
265 if changeType == orthanc.ChangeType.ORTHANC_STARTED:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
266 print('Started')
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
267
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
268 with open('/tmp/sample.dcm', 'rb') as f:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
269 orthanc.RestApiPost('/instances', f.read())
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
270
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
271 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
272 print('Stopped')
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
273
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
274 elif changeType == orthanc.ChangeType.NEW_INSTANCE:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
275 print('A new instance was uploaded: %s' % resource)
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
276
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
277 orthanc.RegisterOnChangeCallback(OnChange)
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
278
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
279
590
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
280
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
281 .. warning::
600
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
282 In releases <= 3.0 of the Python plugin, deadlocks might emerge if
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
283 you call other core primitives of Orthanc (such as the REST API) in
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
284 your callback function. This issue has been `fixed in release 3.1
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
285 <https://hg.orthanc-server.com/orthanc-python/rev/46fe70776d61>`__.
590
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
286
600
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
287 As a **temporary workaround** against such deadlocks in releases <=
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
288 3.0, if you have to call other primitives of Orthanc, you should make
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
289 these calls in a separate thread, passing the pending events to be
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
290 processed through a message queue. Here is the template of a possible
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
291 solution to postpone such deadlocks as much as possible by relying on
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
292 the multithreading primitives of Python::
591
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
293
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
294 import orthanc
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
295 import threading
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
296
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
297 def OnChange(changeType, level, resource):
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
298 # One can safely invoke the "orthanc" module in this function
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
299 orthanc.LogWarning("Hello world")
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
300
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
301 def _OnChange(changeType, level, resource):
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
302 # Invoke the actual "OnChange()" function in a separate thread
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
303 t = threading.Timer(0, function = OnChange, args = (changeType, level, resource))
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
304 t.start()
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
305
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
306 orthanc.RegisterOnChangeCallback(_OnChange)
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
307
600
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
308
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
309 Beware that **this workaround is imperfect** and deadlocks have been
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
310 observed even if using it! Make sure to upgrade your plugin to solve
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
311 this issue for good. Note that this temporary workaround is not needed
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
312 in releases >= 3.1 of the plugin.
4038ae299b18 note about deadlocks in python plugins
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 591
diff changeset
313
591
2397b0e12bc8 solution to avoid deadlock in Python OnChange()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 590
diff changeset
314
590
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
315
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
316 Accessing the content of a new instance
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
317 .......................................
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
318
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
319 .. highlight:: python
345
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
320
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
321 ::
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
322
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
323 import orthanc
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
324 import json
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
325 import pprint
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
326
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
327 def OnStoredInstance(dicom, instanceId):
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
328 print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % (
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
329 instanceId, dicom.GetInstanceSize(),
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
330 dicom.GetInstanceMetadata('TransferSyntax'),
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
331 dicom.GetInstanceMetadata('SopClassUid')))
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
332
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
333 # Print the origin information
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
334 if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
335 print('This instance was received through the DICOM protocol')
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
336 elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API:
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
337 print('This instance was received through the REST API')
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
338
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
339 # Print the DICOM tags
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
340 pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson()))
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
341
f81b533a0fd0 python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 343
diff changeset
342 orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance)
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
343
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
344
590
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
345 .. warning::
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
346 Your callback function will be called synchronously with
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
347 the core of Orthanc. This implies that deadlocks might emerge if
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
348 you call other core primitives of Orthanc in your callback (such
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
349 deadlocks are particular visible in the presence of other plugins
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
350 or Lua scripts). It is thus strongly advised to avoid any call to
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
351 the REST API of Orthanc in the callback. If you have to call other
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
352 primitives of Orthanc, you should make these calls in a separate
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
353 thread, passing the pending events to be processed through a
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
354 message queue.
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
355
404aca6160f0 warning about deadlock in python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 568
diff changeset
356
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
357 Calling pydicom
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
358 ...............
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
359
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
360 .. highlight:: python
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
361
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
362 Here is a sample Python plugin that registers a REST callback to dump
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
363 the content of the dataset of one given DICOM instance stored in
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
364 Orthanc, using `pydicom <https://pydicom.github.io/>`__::
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
365
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
366 import io
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
367 import orthanc
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
368 import pydicom
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
369
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
370 def DecodeInstance(output, uri, **request):
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
371 if request['method'] == 'GET':
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
372 # Retrieve the instance ID from the regular expression (*)
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
373 instanceId = request['groups'][0]
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
374 # Get the content of the DICOM file
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
375 f = orthanc.GetDicomForInstance(instanceId)
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
376 # Parse it using pydicom
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
377 dicom = pydicom.dcmread(io.BytesIO(f))
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
378 # Return a string representation the dataset to the caller
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
379 output.AnswerBuffer(str(dicom), 'text/plain')
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
380 else:
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
381 output.SendMethodNotAllowed('GET')
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
382
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
383 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*)
346
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
384
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
385 .. highlight:: bash
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
386
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
387 This can be called as follows::
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
388
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
389 $ curl http://localhost:8042/pydicom/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5
bdf8757449e3 more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 345
diff changeset
390
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
391
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
392 Auto-routing studies
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
393 ....................
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
394
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
395 .. highlight:: python
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
396
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
397 Here is a sample Python plugin that routes any :ref:`stable study
642
a76d83a00c68 definition of stable resources
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 638
diff changeset
398 <stable-resources>` to a modality named ``samples`` (as declared in the
347
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
399 ``DicomModalities`` configuration option)::
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
400
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
401 import orthanc
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
402
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
403 def OnChange(changeType, level, resourceId):
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
404 if changeType == orthanc.ChangeType.STABLE_STUDY:
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
405 print('Stable study: %s' % resourceId)
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
406 orthanc.RestApiPost('/modalities/sample/store', resourceId)
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
407
04fae9d4b65f auto-routing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 346
diff changeset
408 orthanc.RegisterOnChangeCallback(OnChange)
348
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
409
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
410
539
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
411 Note that, if you want to use an orthanc plugin to transfer the study,
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
412 you should use the ``RestApiPostAfterPlugins()`` method::
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
413
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
414 import orthanc
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
415
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
416 def OnChange(changeType, level, resourceId):
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
417 if changeType == orthanc.ChangeType.STABLE_STUDY:
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
418 print('Stable study: %s' % resourceId)
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
419 orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId)
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
420
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
421 orthanc.RegisterOnChangeCallback(OnChange)
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
422
fd5c1410db5b doc for RestApiPostAfterPlugins
Alain Mazy <alain@mazy.be>
parents: 528
diff changeset
423
352
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
424 Rendering a thumbnail using PIL/Pillow
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
425 ......................................
348
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
426
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
427 .. highlight:: python
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
428
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
429 ::
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
430
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
431 from PIL import Image
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
432 import io
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
433 import orthanc
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
434
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
435 def DecodeInstance(output, uri, **request):
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
436 if request['method'] == 'GET':
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
437 # Retrieve the instance ID from the regular expression (*)
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
438 instanceId = request['groups'][0]
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
439
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
440 # Render the instance, then open it in Python using PIL/Pillow
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
441 png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId)
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
442 image = Image.open(io.BytesIO(png))
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
443
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
444 # Downsize the image as a 64x64 thumbnail
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
445 image.thumbnail((64, 64), Image.ANTIALIAS)
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
446
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
447 # Save the thumbnail as JPEG, then send the buffer to the caller
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
448 jpeg = io.BytesIO()
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
449 image.save(jpeg, format = "JPEG", quality = 80)
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
450 jpeg.seek(0)
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
451 output.AnswerBuffer(jpeg.read(), 'text/plain')
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
452
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
453 else:
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
454 output.SendMethodNotAllowed('GET')
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
455
d8359cecdc89 pillow sample
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 347
diff changeset
456 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*)
351
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
457
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
458
369
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
459 .. _python-introspection:
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
460
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
461 Inspecting the available API
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
462 ............................
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
463
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
464 .. highlight:: python
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
465
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
466 Thanks to Python's introspection primitives, it is possible to inspect
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
467 the API of the ``orthanc`` module in order to dump all the available
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
468 features::
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
469
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
470 import inspect
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
471 import numbers
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
472 import orthanc
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
473
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
474 # Loop over the members of the "orthanc" module
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
475 for (name, obj) in inspect.getmembers(orthanc):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
476 if inspect.isroutine(obj):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
477 print('Function %s():\n Documentation: %s\n' % (name, inspect.getdoc(obj)))
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
478
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
479 elif inspect.isclass(obj):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
480 print('Class %s:\n Documentation: %s' % (name, inspect.getdoc(obj)))
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
481
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
482 # Loop over the members of the class
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
483 for (subname, subobj) in inspect.getmembers(obj):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
484 if isinstance(subobj, numbers.Number):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
485 print(' - Enumeration value %s: %s' % (subname, subobj))
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
486 elif (not subname.startswith('_') and
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
487 inspect.ismethoddescriptor(subobj)):
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
488 print(' - Method %s(): %s' % (subname, inspect.getdoc(subobj)))
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
489 print('')
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
490
373
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
491
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
492 .. _python-scheduler:
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
493
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
494 Scheduling a task for periodic execution
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
495 ........................................
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
496
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
497 .. highlight:: python
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
498
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
499 The following Python script will periodically (every second) run the
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
500 function ``Hello()`` thanks to the ``threading`` module::
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
501
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
502 import orthanc
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
503 import threading
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
504
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
505 TIMER = None
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
506
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
507 def Hello():
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
508 global TIMER
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
509 TIMER = None
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
510 orthanc.LogWarning("In Hello()")
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
511 # Do stuff...
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
512 TIMER = threading.Timer(1, Hello) # Re-schedule after 1 second
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
513 TIMER.start()
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
514
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
515 def OnChange(changeType, level, resource):
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
516 if changeType == orthanc.ChangeType.ORTHANC_STARTED:
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
517 orthanc.LogWarning("Starting the scheduler")
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
518 Hello()
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
519
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
520 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED:
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
521 if TIMER != None:
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
522 orthanc.LogWarning("Stopping the scheduler")
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
523 TIMER.cancel()
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
524
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
525 orthanc.RegisterOnChangeCallback(OnChange)
847996394688 Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 369
diff changeset
526
378
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
527
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
528 .. _python-metadata:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
529
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
530 Filtering and returning metadata
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
531 ................................
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
532
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
533 Besides the main DICOM tags, Orthanc associates some metadata to each
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
534 resource it stores (this includes the date of last update, the
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
535 transfer syntax, the remote AET...). People are often interested in
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
536 getting such metadata while calling the ``/tools/find`` route in the
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
537 :ref:`REST API <rest-find>`, or even in filtering this metadata the
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
538 same way they look for DICOM tags.
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
539
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
540 This feature is not built in the core of Orthanc, as metadata is not
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
541 indexed in the Orthanc database, contrarily to the main DICOM
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
542 tags. Filtering metadata requires a linear search over all the
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
543 matching resources, which induces a cost in the performance.
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
544
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
545 .. highlight:: python
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
546
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
547 Nevertheless, here is a full sample Python script that overwrites the
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
548 ``/tools/find`` route in order to give access to metadata::
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
549
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
550 import json
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
551 import orthanc
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
552 import re
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
553
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
554 # Get the path in the REST API to the given resource that was returned
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
555 # by a call to "/tools/find"
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
556 def GetPath(resource):
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
557 if resource['Type'] == 'Patient':
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
558 return '/patients/%s' % resource['ID']
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
559 elif resource['Type'] == 'Study':
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
560 return '/studies/%s' % resource['ID']
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
561 elif resource['Type'] == 'Series':
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
562 return '/series/%s' % resource['ID']
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
563 elif resource['Type'] == 'Instance':
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
564 return '/instances/%s' % resource['ID']
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
565 else:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
566 raise Exception('Unknown resource level')
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
567
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
568 def FindWithMetadata(output, uri, **request):
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
569 # The "/tools/find" route expects a POST method
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
570 if request['method'] != 'POST':
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
571 output.SendMethodNotAllowed('POST')
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
572 else:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
573 # Parse the query provided by the user, and backup the "Expand" field
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
574 query = json.loads(request['body'])
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
575
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
576 if 'Expand' in query:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
577 originalExpand = query['Expand']
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
578 else:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
579 originalExpand = False
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
580
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
581 # Call the core "/tools/find" route
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
582 query['Expand'] = True
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
583 answers = orthanc.RestApiPost('/tools/find', json.dumps(query))
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
584
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
585 # Loop over the matching resources
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
586 filteredAnswers = []
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
587 for answer in json.loads(answers):
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
588 try:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
589 # Read the metadata that is associated with the resource
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
590 metadata = json.loads(orthanc.RestApiGet('%s/metadata?expand' % GetPath(answer)))
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
591
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
592 # Check whether the metadata matches the regular expressions
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
593 # that were provided in the "Metadata" field of the user request
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
594 isMetadataMatch = True
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
595 if 'Metadata' in query:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
596 for (name, pattern) in query['Metadata'].items():
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
597 if name in metadata:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
598 value = metadata[name]
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
599 else:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
600 value = ''
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
601
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
602 if re.match(pattern, value) == None:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
603 isMetadataMatch = False
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
604 break
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
605
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
606 # If all the metadata matches the provided regular
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
607 # expressions, add the resource to the filtered answers
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
608 if isMetadataMatch:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
609 if originalExpand:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
610 answer['Metadata'] = metadata
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
611 filteredAnswers.append(answer)
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
612 else:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
613 filteredAnswers.append(answer['ID'])
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
614 except:
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
615 # The resource was deleted since the call to "/tools/find"
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
616 pass
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
617
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
618 # Return the filtered answers in the JSON format
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
619 output.AnswerBuffer(json.dumps(filteredAnswers, indent = 3), 'application/json')
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
620
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
621 orthanc.RegisterRestCallback('/tools/find', FindWithMetadata)
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
622
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
623
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
624 **Warning:** In the sample above, the filtering of the metadata is
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
625 done using Python's `library for regular expressions
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
626 <https://docs.python.org/3/library/re.html>`__. It is evidently
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
627 possible to adapt this script in order to use the DICOM conventions
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
628 about `attribute matching
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
629 <http://dicom.nema.org/medical/dicom/2019e/output/chtml/part04/sect_C.2.2.2.html>`__.
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
630
469
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
631 .. highlight:: bash
378
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
632
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
633 Here is a sample call to retrieve all the studies that were last
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
634 updated in 2019 thanks to this Python script::
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
635
16dc3561b41e Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 375
diff changeset
636 $ curl http://localhost:8042/tools/find -d '{"Level":"Study","Query":{},"Expand":true,"Metadata":{"LastUpdate":"^2019.*$"}}'
369
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
637
181b02cea7ab inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 364
diff changeset
638
469
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
639 .. _python-paging:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
640
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
641 Implementing basic paging
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
642 .........................
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
643
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
644 .. highlight:: python
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
645
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
646 As explained in the FAQ, the :ref:`Orthanc Explorer interface is
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
647 low-level <improving-interface>`, and is not adapted for
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
648 end-users. One common need is to implement paging of studies, which
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
649 calls for server-side sorting of studies. This can be done using the
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
650 following sample Python plugin that registers a new route
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
651 ``/sort-studies`` in the REST API of Orthanc::
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
652
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
653 import json
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
654 import orthanc
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
655
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
656 def GetStudyDate(study):
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
657 if 'StudyDate' in study['MainDicomTags']:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
658 return study['MainDicomTags']['StudyDate']
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
659 else:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
660 return ''
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
661
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
662 def SortStudiesByDate(output, uri, **request):
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
663 if request['method'] == 'GET':
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
664 # Retrieve all the studies
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
665 studies = json.loads(orthanc.RestApiGet('/studies?expand'))
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
666
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
667 # Sort the studies according to the "StudyDate" DICOM tag
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
668 studies = sorted(studies, key = GetStudyDate)
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
669
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
670 # Read the limit/offset arguments provided by the user
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
671 offset = 0
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
672 if 'offset' in request['get']:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
673 offset = int(request['get']['offset'])
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
674
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
675 limit = 0
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
676 if 'limit' in request['get']:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
677 limit = int(request['get']['limit'])
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
678
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
679 # Truncate the list of studies
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
680 if limit == 0:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
681 studies = studies[offset : ]
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
682 else:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
683 studies = studies[offset : offset + limit]
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
684
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
685 # Return the truncated list of studies
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
686 output.AnswerBuffer(json.dumps(studies), 'application/json')
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
687 else:
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
688 output.SendMethodNotAllowed('GET')
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
689
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
690 orthanc.RegisterRestCallback('/sort-studies', SortStudiesByDate)
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
691
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
692
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
693 .. highlight:: bash
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
694
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
695 Here is a sample call to this new REST route, that could be issued by
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
696 any JavaScript framework (the ``json_pp`` command-line pretty-prints a
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
697 JSON file)::
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
698
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
699 $ curl http://localhost:8042/sort-studies | json_pp
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
700
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
701 This route also implement paging (i.e. it can limit and offset the
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
702 returned studies)::
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
703
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
704 $ curl 'http://localhost:8042/sort-studies?offset=2&limit=2' | json_pp
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
705
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
706 Obviously, this basic sample can be improved in many ways. To improve
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
707 performance, one could for instance cache the result of
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
708 ``/studies?expand`` in memory by :ref:`listening to changes
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
709 <python-changes>` in the list of studies
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
710 (cf. ``orthanc.ChangeType.NEW_STUDY`` and
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
711 ``orthanc.ChangeType.DELETED``).
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
712
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
713
551
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
714 .. _python_excel:
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
715
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
716 Creating a Microsoft Excel report
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
717 .................................
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
718
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
719 .. highlight:: python
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
720
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
721 As Orthanc plugins have access to any installed Python module, it is
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
722 very easy to implement a server-side plugin that generates a report in
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
723 the Microsoft Excel ``.xls`` format. Here is a working example::
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
724
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
725 import StringIO
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
726 import json
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
727 import orthanc
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
728 import xlwt
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
729
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
730 def CreateExcelReport(output, uri, **request):
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
731 if request['method'] != 'GET' :
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
732 output.SendMethodNotAllowed('GET')
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
733 else:
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
734 # Create an Excel writer
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
735 excel = xlwt.Workbook()
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
736 sheet = excel.add_sheet('Studies')
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
737
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
738 # Loop over the studies stored in Orthanc
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
739 row = 0
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
740 studies = orthanc.RestApiGet('/studies?expand')
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
741 for study in json.loads(studies):
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
742 sheet.write(row, 0, study['PatientMainDicomTags'].get('PatientID'))
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
743 sheet.write(row, 1, study['PatientMainDicomTags'].get('PatientName'))
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
744 sheet.write(row, 2, study['MainDicomTags'].get('StudyDescription'))
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
745 row += 1
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
746
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
747 # Serialize the Excel workbook to a string, and return it to the caller
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
748 # https://stackoverflow.com/a/15649139/881731
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
749 b = StringIO.StringIO()
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
750 excel.save(b)
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
751 output.AnswerBuffer(b.getvalue(), 'application/vnd.ms-excel')
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
752
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
753 orthanc.RegisterRestCallback('/report.xls', CreateExcelReport)
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
754
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
755 If opening the ``http://localhost:8042/report.xls`` URI, this Python
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
756 will generate a workbook with one sheet that contains the list of
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
757 studies, with the patient ID, the patient name and the study
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
758 description.
d0332c5a2cb8 Python plugin: excel generation example
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 539
diff changeset
759
469
46949efa5818 Implementing basic paging using a Python script
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
760
556
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
761 .. _python_authorization:
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
762
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
763 Forbid or allow access to REST resources (authorization)
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
764 ........................................................
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
765
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
766 .. highlight:: python
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
767
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
768 The following Python script installs a callback that is triggered
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
769 whenever the HTTP server of Orthanc is accessed::
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
770
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
771 import orthanc
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
772 import pprint
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
773
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
774 def Filter(uri, **request):
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
775 print('User trying to access URI: %s' % uri)
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
776 pprint.pprint(request)
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
777 return True # False to forbid access
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
778
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
779 orthanc.RegisterIncomingHttpRequestFilter(Filter)
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
780
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
781 If access is not granted, the ``Filter`` callback must return
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
782 ``False``. As a consequence, the HTTP status code would be set to
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
783 ``403 Forbidden``. If access is granted, the ``Filter`` must return
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
784 ``true``. The ``request`` argument contains more information about the
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
785 request (such as the HTTP headers, the IP address of the caller and
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
786 the GET arguments).
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
787
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
788 Note that this is similar to the ``IncomingHttpRequestFilter()``
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
789 callback that is available in :ref:`Lua scripts <lua-filter-rest>`.
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
790
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
791 Thanks to Python, it is extremely easy to call remote Web services for
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
792 authorization. Here is an example using the ``requests`` library::
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
793
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
794 import json
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
795 import orthanc
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
796 import requests
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
797
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
798 def Filter(uri, **request):
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
799 body = {
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
800 'uri' : uri,
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
801 'headers' : request['headers']
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
802 }
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
803 r = requests.post('http://localhost:8000/authorize',
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
804 data = json.dumps(body))
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
805 return r.json() ['granted'] # Must be a Boolean
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
806
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
807 orthanc.RegisterIncomingHttpRequestFilter(Filter)
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
808
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
809 .. highlight:: javascript
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
810
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
811 This filter could be used together with the following Web service
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
812 implemented using `Node.js
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
813 <https://en.wikipedia.org/wiki/Node.js>`__::
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
814
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
815 const http = require('http');
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
816
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
817 const requestListener = function(req, res) {
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
818 let body = '';
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
819 req.on('data', function(chunk) {
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
820 body += chunk;
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
821 });
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
822 req.on('end', function() {
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
823 console.log(JSON.parse(body));
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
824 var answer = {
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
825 'granted' : false // Forbid access
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
826 };
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
827 res.writeHead(200);
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
828 res.end(JSON.stringify(answer));
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
829 });
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
830 }
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
831
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
832 http.createServer(requestListener).listen(8000);
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
833
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
834
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
835 .. _python_create_dicom:
556
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
836
696
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 695
diff changeset
837 Creating DICOM instances (new in 3.2)
695
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
838 .....................................
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
839
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
840 .. highlight:: python
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
841
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
842 The following sample Python script will write on the disk a new DICOM
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
843 instance including the traditional Lena sample image, and will decode
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
844 the single frame of this DICOM instance::
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
845
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
846 import json
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
847 import orthanc
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
848
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
849 def OnChange(changeType, level, resource):
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
850 if changeType == orthanc.ChangeType.ORTHANC_STARTED:
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
851 tags = {
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
852 'SOPClassUID' : '1.2.840.10008.5.1.4.1.1.1',
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
853 'PatientID' : 'HELLO',
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
854 'PatientName' : 'WORLD',
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
855 }
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
856
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
857 with open('Lena.png', 'rb') as f:
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
858 img = orthanc.UncompressImage(f.read(), orthanc.ImageFormat.PNG)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
859
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
860 s = orthanc.CreateDicom(json.dumps(tags), img, orthanc.CreateDicomFlags.GENERATE_IDENTIFIERS)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
861
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
862 with open('/tmp/sample.dcm', 'wb') as f:
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
863 f.write(s)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
864
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
865 dicom = orthanc.CreateDicomInstance(s)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
866 frame = dicom.GetInstanceDecodedFrame(0)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
867 print('Size of the frame: %dx%d' % (frame.GetImageWidth(), frame.GetImageHeight()))
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
868
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
869 orthanc.RegisterOnChangeCallback(OnChange)
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
870
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
871
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
872
f4fc12ed00ee creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 678
diff changeset
873
556
6a3d48510b0b Python sample: "Forbid or allow access to REST resources (authorization)", deprecating advanced authorization plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 555
diff changeset
874
351
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
875 Performance and concurrency
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
876 ---------------------------
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
877
471
a735476a0e6e note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
878 **Important:** This section only applies to UNIX-like systems. The
a735476a0e6e note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
879 ``multiprocessing`` package will not work on Microsoft Windows as the
a735476a0e6e note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
880 latter OS has a different model for `forking processes
a735476a0e6e note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
881 <https://en.wikipedia.org/wiki/Fork_(system_call)>`__.
a735476a0e6e note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 462
diff changeset
882
555
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
883 Using slave processes
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
884 .....................
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
885
351
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
886 .. highlight:: python
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
887
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
888 Let us consider the following sample Python script that makes a
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
889 CPU-intensive computation on a REST callback::
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
890
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
891 import math
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
892 import orthanc
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
893 import time
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
894
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
895 # CPU-intensive computation taking about 4 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
896 def SlowComputation():
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
897 start = time.time()
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
898 for i in range(1000):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
899 for j in range(30000):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
900 math.sqrt(float(j))
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
901 end = time.time()
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
902 duration = (end - start)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
903 return 'computation done in %.03f seconds\n' % duration
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
904
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
905 def OnRest(output, uri, **request):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
906 answer = SlowComputation()
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
907 output.AnswerBuffer(answer, 'text/plain')
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
908
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
909 orthanc.RegisterRestCallback('/computation', OnRest)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
910
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
911
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
912 .. highlight:: text
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
913
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
914 Calling this REST route from the command-line returns the time that is
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
915 needed to compute 30 million times a squared root on your CPU::
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
916
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
917 $ curl http://localhost:8042/computation
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
918 computation done in 4.208 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
919
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
920 Now, let us call this route three times concurrently (we use bash)::
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
921
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
922 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation )
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
923 computation done in 11.262 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
924 computation done in 12.457 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
925 computation done in 13.360 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
926
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
927 As can be seen, the computation time has tripled. This means that the
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
928 computations were not distributed across the available CPU cores.
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
929 This might seem surprising, as Orthanc is a threaded server (in
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
930 Orthanc, a pool of C++ threads serves concurrent requests).
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
931
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
932 The explanation is that the Python interpreter (`CPython
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
933 <https://en.wikipedia.org/wiki/CPython>`__ actually) is built on the
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
934 top of a so-called `Global Interpreter Lock (GIL)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
935 <https://en.wikipedia.org/wiki/Global_interpreter_lock>`__. The GIL is
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
936 basically a mutex that protects all the calls to the Python
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
937 interpreter. If multiple C++ threads from Orthanc call a Python
353
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
938 callback, only one can proceed at any given time. Note however that
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
939 the GIL only applies to the Python script: The baseline REST API of
0122c668f4ec python licensing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 352
diff changeset
940 Orthanc is not affected by the GIL.
351
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
941
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
942 .. highlight:: python
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
943
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
944 The solution is to use the `multiprocessing primitives
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
945 <https://docs.python.org/3/library/multiprocessing.html>`__ of Python.
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
946 The "master" Python interpreter that is initially started by the
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
947 Orthanc plugin, can start several `children processes
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
948 <https://en.wikipedia.org/wiki/Process_(computing)>`__, each of these
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
949 processes running a separate Python interpreter. This allows to
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
950 offload intensive computations from the "master" Python interpreter of
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
951 Orthanc onto those "slave" interpreters. The ``multiprocessing``
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
952 library is actually quite straightforward to use::
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
953
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
954 import math
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
955 import multiprocessing
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
956 import orthanc
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
957 import signal
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
958 import time
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
959
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
960 # CPU-intensive computation taking about 4 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
961 # (same code as above)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
962 def SlowComputation():
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
963 start = time.time()
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
964 for i in range(1000):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
965 for j in range(30000):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
966 math.sqrt(float(j))
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
967 end = time.time()
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
968 duration = (end - start)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
969 return 'computation done in %.03f seconds\n' % duration
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
970
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
971 # Ignore CTRL+C in the slave processes
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
972 def Initializer():
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
973 signal.signal(signal.SIGINT, signal.SIG_IGN)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
974
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
975 # Create a pool of 4 slave Python interpreters
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
976 POOL = multiprocessing.Pool(4, initializer = Initializer)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
977
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
978 def OnRest(output, uri, **request):
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
979 # Offload the call to "SlowComputation" onto one slave process.
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
980 # The GIL is unlocked until the slave sends its answer back.
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
981 answer = POOL.apply(SlowComputation)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
982 output.AnswerBuffer(answer, 'text/plain')
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
983
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
984 orthanc.RegisterRestCallback('/computation', OnRest)
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
985
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
986 .. highlight:: text
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
987
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
988 Here is now the result of calling this route three times concurrently::
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
989
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
990 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation )
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
991 computation done in 4.211 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
992 computation done in 4.215 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
993 computation done in 4.225 seconds
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
994
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
995 As can be seen, the calls to the Python computation now fully run in
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
996 parallel (the time is cut down from 12 seconds to 4 seconds, the same
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
997 as for one isolated request).
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
998
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
999 Note also how the ``multiprocessing`` library allows to make a fine
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
1000 control over the computational resources that are available to the
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
1001 Python script: The number of "slave" interpreters can be easily
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
1002 changed in the constructor of the ``multiprocessing.Pool`` object, and
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
1003 are fully independent of the threads used by the Orthanc server.
e2863083fa30 multiprocessing
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 350
diff changeset
1004
555
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1005 Obviously, an in-depth discussion about the ``multiprocessing``
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1006 library is out of the scope of this document. There are many
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1007 references available on Internet. Also, note that ``threading`` is not
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1008 useful here, as Python multithreading is also limited by the GIL, and
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1009 is more targeted at dealing with costly I/O operations or with the
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1010 :ref:`scheduling of commands <python-scheduler>`.
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1011
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1012
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1013 Slave processes and the "orthanc" module
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1014 ........................................
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1015
352
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1016 .. highlight:: python
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1017
555
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1018 Very importantly, pay attention to the fact that **only the "master"
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1019 Python interpreter has access to the Orthanc SDK**. The "slave"
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1020 processes have no access to the ``orthanc`` module.
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1021
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1022 You must write your Python plugin so as that all the calls to
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1023 ``orthanc`` are moved from the slaves process to the master
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1024 process. For instance, here is how you would parse a DICOM file in a
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1025 slave process::
352
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1026
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1027 import pydicom
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1028 import io
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1029
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1030 def OffloadedDicomParsing(dicom):
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1031 # No access to the "orthanc" library here, as we are in the slave process
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1032 dataset = pydicom.dcmread(io.BytesIO(dicom))
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1033 return str(dataset)
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1034
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1035 def OnRest(output, uri, **request):
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1036 # The call to "orthanc.RestApiGet()" is only possible in the master process
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1037 dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file')
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1038 answer = POOL.apply(OffloadedDicomParsing, args = (dicom, ))
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1039 output.AnswerBuffer(answer, 'text/plain')
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1040
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1041 Communication primitives such as ``multiprocessing.Queue`` are
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1042 available to exchange messages from the "slave" Python interpreters to
555
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1043 the "master" Python interpreter for more advanced scenarios.
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1044
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1045 NB: Starting with release 3.0 of the Python plugin, it is possible to
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1046 call the REST API of Orthanc from a slave process in a more direct
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1047 way. The function ``orthanc.GenerateRestApiAuthorizationToken()`` can
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1048 be used to create an authorization token that provides full access to
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1049 the REST API of Orthanc (without have to set credentials in your
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1050 plugin). Any HTTP client library for Python, such as `requests
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1051 <https://requests.readthedocs.io/en/master/>`__, can then be used to
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1052 access the REST API of Orthanc. Here is a minimal example::
352
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 351
diff changeset
1053
555
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1054 import json
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1055 import multiprocessing
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1056 import orthanc
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1057 import requests
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1058 import signal
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1059
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1060 TOKEN = orthanc.GenerateRestApiAuthorizationToken()
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1061
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1062 def SlaveProcess():
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1063 r = requests.get('http://localhost:8042/instances',
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1064 headers = { 'Authorization' : TOKEN })
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1065 return json.dumps(r.json())
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1066
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1067 def Initializer():
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1068 signal.signal(signal.SIGINT, signal.SIG_IGN)
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1069
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1070 POOL = multiprocessing.Pool(4, initializer = Initializer)
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1071
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1072 def OnRest(output, uri, **request):
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1073 answer = POOL.apply(SlaveProcess)
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1074 output.AnswerBuffer(answer, 'text/plain')
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1075
6fb469a3c382 Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents: 554
diff changeset
1076 orthanc.RegisterRestCallback('/computation', OnRest)