Mercurial > hg > orthanc-book
annotate Sphinx/source/plugins/python.rst @ 378:16dc3561b41e
Filtering and returning metadata using Python
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 08 Apr 2020 08:49:33 +0200 |
parents | 766fe39fdf35 |
children | c9fe3d0d0fa1 |
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 |
345 | 21 <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.5.7/Plugins/Include/orthanc/OrthancCPlugin.h>`__ |
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 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
25 As of initial release 1.0 of the plugin, the coverage of the C SDK is |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
26 about 75% (105 functions are automatically wrapped in Python out of a |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
27 total of 139 functions in the Orthanc SDK 1.5.7). |
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 | 40 Licensing |
41 --------- | |
42 | |
43 Pay attention to the fact that this plugin is licensed under the terms | |
44 of the `AGPL license | |
45 <https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License>`__. | |
46 | |
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 | 51 community under the terms of the AGPL license. |
52 | |
53 We suggest you to put the source code of your Python scripts on the | |
54 dedicated `"OrthancContributed" repository on GitHub | |
55 <https://github.com/jodogne/OrthancContributed/tree/master/Plugins>`__, | |
56 and/or to send it to the `Orthanc Users | |
57 <https://groups.google.com/forum/#!forum/orthanc-users>`__ discussion | |
58 group. | |
353 | 59 |
60 Check out the :ref:`FAQ about licensing <licensing>` for more context. | |
61 | |
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 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
111 Compiling from source |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
112 ..................... |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
113 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
114 .. highlight:: bash |
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 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
|
117 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
|
118 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
|
119 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
120 $ mkdir Build |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
121 $ cd Build |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
122 $ 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
|
123 $ make |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
124 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
125 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
|
126 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
|
127 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
|
128 ``python3.7``. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
129 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
130 The compilation will produce the shared library ``OrthancPython``, |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
131 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
|
132 :ref:`configuration option <configuration>` of Orthanc. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
133 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
134 **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
|
135 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
|
136 ``PYTHON_VERSION`` argument that was given to CMake. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
137 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
138 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
139 Microsoft Windows |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
140 ................. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
141 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
142 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
|
143 <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
|
144 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
145 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
|
146 one version of the Python interpreter. This version is clearly |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
147 indicated in the name of the folder. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
148 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
149 As of release 1.0, the Orthanc project only provides pre-compiled |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
150 binaries for Microsoft Windows 32bit and Python 2.7. Even though this |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
151 version of Python is not supported anymore, it can still run on all |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
152 the versions of Microsoft Windows that have been released for more |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
153 than 10 years. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
154 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
155 .. highlight:: text |
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 You are of course free to compile the plugin from sources if you need |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
158 a more recent version. You'll have to explicitly specify the path to |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
159 your Python installation while invoking CMake. For instance:: |
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 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
|
162 -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
|
163 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
164 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
165 Configuration options |
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 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
168 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
|
169 are the following: |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
170 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
171 * ``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
|
172 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
|
173 started. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
174 |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
175 * ``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
|
176 verbose. |
234de55ed125
usage of the python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
354
diff
changeset
|
177 |
343
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
178 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
179 Samples |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
180 ------- |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
181 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
182 Extending the REST API |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
183 ...................... |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
184 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
185 .. highlight:: python |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
186 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
187 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
|
188 REST API:: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
189 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
190 import orthanc |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
191 import pprint |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
192 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
193 def OnRest(output, uri, **request): |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
194 pprint.pprint(request) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
195 print('Accessing uri: %s' % uri) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
196 output.AnswerBuffer('ok\n', 'text/plain') |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
197 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
198 orthanc.RegisterRestCallback('/(to)(t)o', OnRest) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
199 orthanc.RegisterRestCallback('/tata', OnRest) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
200 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
201 .. highlight:: json |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
202 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
203 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
|
204 (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
|
205 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
206 { |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
207 "Plugins" : [ "." ], |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
208 "PythonScript" : "rest.py", |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
209 "PythonVerbose" : false |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
210 } |
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 .. highlight:: bash |
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 The route can then be accessed as:: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
215 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
216 $ curl http://localhost:8042/toto |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
217 ok |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
218 |
345 | 219 |
220 Listening to changes | |
221 .................... | |
222 | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
223 .. highlight:: python |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
224 |
345 | 225 This sample uploads a DICOM file as soon as Orthanc is started:: |
226 | |
227 import orthanc | |
228 | |
229 def OnChange(changeType, level, resource): | |
230 if changeType == orthanc.ChangeType.ORTHANC_STARTED: | |
231 print('Started') | |
232 | |
233 with open('/tmp/sample.dcm', 'rb') as f: | |
234 orthanc.RestApiPost('/instances', f.read()) | |
235 | |
236 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: | |
237 print('Stopped') | |
238 | |
239 elif changeType == orthanc.ChangeType.NEW_INSTANCE: | |
240 print('A new instance was uploaded: %s' % resource) | |
241 | |
242 orthanc.RegisterOnChangeCallback(OnChange) | |
243 | |
244 | |
245 Accessing the content of a new instance | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
246 ....................................... |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
247 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
248 .. highlight:: python |
345 | 249 |
250 :: | |
251 | |
252 import orthanc | |
253 import json | |
254 import pprint | |
255 | |
256 def OnStoredInstance(dicom, instanceId): | |
257 print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % ( | |
258 instanceId, dicom.GetInstanceSize(), | |
259 dicom.GetInstanceMetadata('TransferSyntax'), | |
260 dicom.GetInstanceMetadata('SopClassUid'))) | |
261 | |
262 # Print the origin information | |
263 if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL: | |
264 print('This instance was received through the DICOM protocol') | |
265 elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API: | |
266 print('This instance was received through the REST API') | |
267 | |
268 # Print the DICOM tags | |
269 pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson())) | |
270 | |
271 orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance) | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
272 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
273 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
274 Calling pydicom |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
275 ............... |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
276 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
277 .. highlight:: python |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
278 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
279 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
|
280 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
|
281 Orthanc, using `pydicom <https://pydicom.github.io/>`__:: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
282 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
283 import io |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
284 import orthanc |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
285 import pydicom |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
286 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
287 def DecodeInstance(output, uri, **request): |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
288 if request['method'] == 'GET': |
347 | 289 # Retrieve the instance ID from the regular expression (*) |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
290 instanceId = request['groups'][0] |
347 | 291 # Get the content of the DICOM file |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
292 f = orthanc.GetDicomForInstance(instanceId) |
347 | 293 # Parse it using pydicom |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
294 dicom = pydicom.dcmread(io.BytesIO(f)) |
347 | 295 # Return a string representation the dataset to the caller |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
296 output.AnswerBuffer(str(dicom), 'text/plain') |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
297 else: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
298 output.SendMethodNotAllowed('GET') |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
299 |
347 | 300 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
301 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
302 .. highlight:: bash |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
303 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
304 This can be called as follows:: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
305 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
306 $ curl http://localhost:8042/pydicom/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
307 |
347 | 308 |
309 Auto-routing studies | |
310 .................... | |
311 | |
312 .. highlight:: python | |
313 | |
314 Here is a sample Python plugin that routes any :ref:`stable study | |
315 <lua-callbacks>` to a modality named ``samples`` (as declared in the | |
316 ``DicomModalities`` configuration option):: | |
317 | |
318 import orthanc | |
319 | |
320 def OnChange(changeType, level, resourceId): | |
321 if changeType == orthanc.ChangeType.STABLE_STUDY: | |
322 print('Stable study: %s' % resourceId) | |
323 orthanc.RestApiPost('/modalities/sample/store', resourceId) | |
324 | |
325 orthanc.RegisterOnChangeCallback(OnChange) | |
348 | 326 |
327 | |
352 | 328 Rendering a thumbnail using PIL/Pillow |
329 ...................................... | |
348 | 330 |
331 .. highlight:: python | |
332 | |
333 :: | |
334 | |
335 from PIL import Image | |
336 import io | |
337 import orthanc | |
338 | |
339 def DecodeInstance(output, uri, **request): | |
340 if request['method'] == 'GET': | |
341 # Retrieve the instance ID from the regular expression (*) | |
342 instanceId = request['groups'][0] | |
343 | |
344 # Render the instance, then open it in Python using PIL/Pillow | |
345 png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId) | |
346 image = Image.open(io.BytesIO(png)) | |
347 | |
348 # Downsize the image as a 64x64 thumbnail | |
349 image.thumbnail((64, 64), Image.ANTIALIAS) | |
350 | |
351 # Save the thumbnail as JPEG, then send the buffer to the caller | |
352 jpeg = io.BytesIO() | |
353 image.save(jpeg, format = "JPEG", quality = 80) | |
354 jpeg.seek(0) | |
355 output.AnswerBuffer(jpeg.read(), 'text/plain') | |
356 | |
357 else: | |
358 output.SendMethodNotAllowed('GET') | |
359 | |
360 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) | |
351 | 361 |
362 | |
369
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
363 .. _python-introspection: |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
364 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
365 Inspecting the available API |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
366 ............................ |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
367 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
368 .. highlight:: python |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
369 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
370 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
|
371 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
|
372 features:: |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
373 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
374 import inspect |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
375 import numbers |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
376 import orthanc |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
377 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
378 # Loop over the members of the "orthanc" module |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
379 for (name, obj) in inspect.getmembers(orthanc): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
380 if inspect.isroutine(obj): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
381 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
|
382 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
383 elif inspect.isclass(obj): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
384 print('Class %s:\n Documentation: %s' % (name, inspect.getdoc(obj))) |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
385 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
386 # Loop over the members of the class |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
387 for (subname, subobj) in inspect.getmembers(obj): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
388 if isinstance(subobj, numbers.Number): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
389 print(' - Enumeration value %s: %s' % (subname, subobj)) |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
390 elif (not subname.startswith('_') and |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
391 inspect.ismethoddescriptor(subobj)): |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
392 print(' - Method %s(): %s' % (subname, inspect.getdoc(subobj))) |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
393 print('') |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
394 |
373
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
395 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
396 .. _python-scheduler: |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
397 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
398 Scheduling a task for periodic execution |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
399 ........................................ |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
400 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
401 .. highlight:: python |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
402 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
403 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
|
404 function ``Hello()`` thanks to the ``threading`` module:: |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
405 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
406 import orthanc |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
407 import threading |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
408 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
409 TIMER = None |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
410 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
411 def Hello(): |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
412 global TIMER |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
413 TIMER = None |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
414 orthanc.LogWarning("In Hello()") |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
415 # Do stuff... |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
416 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
|
417 TIMER.start() |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
418 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
419 def OnChange(changeType, level, resource): |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
420 if changeType == orthanc.ChangeType.ORTHANC_STARTED: |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
421 orthanc.LogWarning("Starting the scheduler") |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
422 Hello() |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
423 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
424 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
425 if TIMER != None: |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
426 orthanc.LogWarning("Stopping the scheduler") |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
427 TIMER.cancel() |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
428 |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
429 orthanc.RegisterOnChangeCallback(OnChange) |
847996394688
Scheduling a task for periodic execution
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
369
diff
changeset
|
430 |
378
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
431 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
432 .. _python-metadata: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
433 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
434 Filtering and returning metadata |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
435 ................................ |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
436 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
437 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
|
438 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
|
439 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
|
440 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
|
441 :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
|
442 same way they look for DICOM tags. |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
443 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
444 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
|
445 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
|
446 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
|
447 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
|
448 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
449 .. highlight:: python |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
450 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
451 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
|
452 ``/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
|
453 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
454 import json |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
455 import orthanc |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
456 import re |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
457 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
458 # 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
|
459 # by a call to "/tools/find" |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
460 def GetPath(resource): |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
461 if resource['Type'] == 'Patient': |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
462 return '/patients/%s' % resource['ID'] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
463 elif resource['Type'] == 'Study': |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
464 return '/studies/%s' % resource['ID'] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
465 elif resource['Type'] == 'Series': |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
466 return '/series/%s' % resource['ID'] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
467 elif resource['Type'] == 'Instance': |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
468 return '/instances/%s' % resource['ID'] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
469 else: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
470 raise Exception('Unknown resource level') |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
471 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
472 def FindWithMetadata(output, uri, **request): |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
473 # 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
|
474 if request['method'] != 'POST': |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
475 output.SendMethodNotAllowed('POST') |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
476 else: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
477 # 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
|
478 query = json.loads(request['body']) |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
479 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
480 if 'Expand' in query: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
481 originalExpand = query['Expand'] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
482 else: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
483 originalExpand = False |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
484 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
485 # Call the core "/tools/find" route |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
486 query['Expand'] = True |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
487 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
|
488 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
489 # Loop over the matching resources |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
490 filteredAnswers = [] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
491 for answer in json.loads(answers): |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
492 try: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
493 # 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
|
494 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
|
495 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
496 # 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
|
497 # 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
|
498 isMetadataMatch = True |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
499 if 'Metadata' in query: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
500 for (name, pattern) in query['Metadata'].items(): |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
501 if name in metadata: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
502 value = metadata[name] |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
503 else: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
504 value = '' |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
505 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
506 if re.match(pattern, value) == None: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
507 isMetadataMatch = False |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
508 break |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
509 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
510 # 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
|
511 # 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
|
512 if isMetadataMatch: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
513 if originalExpand: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
514 answer['Metadata'] = metadata |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
515 filteredAnswers.append(answer) |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
516 else: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
517 filteredAnswers.append(answer['ID']) |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
518 except: |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
519 # 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
|
520 pass |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
521 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
522 # 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
|
523 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
|
524 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
525 orthanc.RegisterRestCallback('/tools/find', FindWithMetadata) |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
526 |
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 **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
|
529 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
|
530 <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
|
531 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
|
532 about `attribute matching |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
533 <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
|
534 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
535 .. highlight:: python |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
536 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
537 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
|
538 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
|
539 |
16dc3561b41e
Filtering and returning metadata using Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
540 $ 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
|
541 |
181b02cea7ab
inspecting Python API
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
364
diff
changeset
|
542 |
351 | 543 Performance and concurrency |
544 --------------------------- | |
545 | |
546 .. highlight:: python | |
547 | |
548 Let us consider the following sample Python script that makes a | |
549 CPU-intensive computation on a REST callback:: | |
550 | |
551 import math | |
552 import orthanc | |
553 import time | |
554 | |
555 # CPU-intensive computation taking about 4 seconds | |
556 def SlowComputation(): | |
557 start = time.time() | |
558 for i in range(1000): | |
559 for j in range(30000): | |
560 math.sqrt(float(j)) | |
561 end = time.time() | |
562 duration = (end - start) | |
563 return 'computation done in %.03f seconds\n' % duration | |
564 | |
565 def OnRest(output, uri, **request): | |
566 answer = SlowComputation() | |
567 output.AnswerBuffer(answer, 'text/plain') | |
568 | |
569 orthanc.RegisterRestCallback('/computation', OnRest) | |
570 | |
571 | |
572 .. highlight:: text | |
573 | |
574 Calling this REST route from the command-line returns the time that is | |
575 needed to compute 30 million times a squared root on your CPU:: | |
576 | |
577 $ curl http://localhost:8042/computation | |
578 computation done in 4.208 seconds | |
579 | |
580 Now, let us call this route three times concurrently (we use bash):: | |
581 | |
582 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation ) | |
583 computation done in 11.262 seconds | |
584 computation done in 12.457 seconds | |
585 computation done in 13.360 seconds | |
586 | |
587 As can be seen, the computation time has tripled. This means that the | |
588 computations were not distributed across the available CPU cores. | |
589 This might seem surprising, as Orthanc is a threaded server (in | |
590 Orthanc, a pool of C++ threads serves concurrent requests). | |
591 | |
592 The explanation is that the Python interpreter (`CPython | |
593 <https://en.wikipedia.org/wiki/CPython>`__ actually) is built on the | |
594 top of a so-called `Global Interpreter Lock (GIL) | |
595 <https://en.wikipedia.org/wiki/Global_interpreter_lock>`__. The GIL is | |
596 basically a mutex that protects all the calls to the Python | |
597 interpreter. If multiple C++ threads from Orthanc call a Python | |
353 | 598 callback, only one can proceed at any given time. Note however that |
599 the GIL only applies to the Python script: The baseline REST API of | |
600 Orthanc is not affected by the GIL. | |
351 | 601 |
602 .. highlight:: python | |
603 | |
604 The solution is to use the `multiprocessing primitives | |
605 <https://docs.python.org/3/library/multiprocessing.html>`__ of Python. | |
606 The "master" Python interpreter that is initially started by the | |
607 Orthanc plugin, can start several `children processes | |
608 <https://en.wikipedia.org/wiki/Process_(computing)>`__, each of these | |
609 processes running a separate Python interpreter. This allows to | |
610 offload intensive computations from the "master" Python interpreter of | |
611 Orthanc onto those "slave" interpreters. The ``multiprocessing`` | |
612 library is actually quite straightforward to use:: | |
613 | |
614 import math | |
615 import multiprocessing | |
616 import orthanc | |
617 import signal | |
618 import time | |
619 | |
620 # CPU-intensive computation taking about 4 seconds | |
621 # (same code as above) | |
622 def SlowComputation(): | |
623 start = time.time() | |
624 for i in range(1000): | |
625 for j in range(30000): | |
626 math.sqrt(float(j)) | |
627 end = time.time() | |
628 duration = (end - start) | |
629 return 'computation done in %.03f seconds\n' % duration | |
630 | |
631 # Ignore CTRL+C in the slave processes | |
632 def Initializer(): | |
633 signal.signal(signal.SIGINT, signal.SIG_IGN) | |
634 | |
635 # Create a pool of 4 slave Python interpreters | |
636 POOL = multiprocessing.Pool(4, initializer = Initializer) | |
637 | |
638 def OnRest(output, uri, **request): | |
639 # Offload the call to "SlowComputation" onto one slave process. | |
640 # The GIL is unlocked until the slave sends its answer back. | |
641 answer = POOL.apply(SlowComputation) | |
642 output.AnswerBuffer(answer, 'text/plain') | |
643 | |
644 orthanc.RegisterRestCallback('/computation', OnRest) | |
645 | |
646 .. highlight:: text | |
647 | |
648 Here is now the result of calling this route three times concurrently:: | |
649 | |
650 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation ) | |
651 computation done in 4.211 seconds | |
652 computation done in 4.215 seconds | |
653 computation done in 4.225 seconds | |
654 | |
655 As can be seen, the calls to the Python computation now fully run in | |
656 parallel (the time is cut down from 12 seconds to 4 seconds, the same | |
657 as for one isolated request). | |
658 | |
659 Note also how the ``multiprocessing`` library allows to make a fine | |
660 control over the computational resources that are available to the | |
661 Python script: The number of "slave" interpreters can be easily | |
662 changed in the constructor of the ``multiprocessing.Pool`` object, and | |
663 are fully independent of the threads used by the Orthanc server. | |
664 | |
352 | 665 .. highlight:: python |
666 | |
667 Very importantly, pay attention to the fact that only the "master" | |
668 Python interpreter has access to the Orthanc SDK. For instance, here | |
669 is how you would parse a DICOM file in a slave process:: | |
670 | |
671 import pydicom | |
672 import io | |
673 | |
674 def OffloadedDicomParsing(dicom): | |
675 # No access to the "orthanc" library here, as we are in the slave process | |
676 dataset = pydicom.dcmread(io.BytesIO(dicom)) | |
677 return str(dataset) | |
678 | |
679 def OnRest(output, uri, **request): | |
680 # The call to "orthanc.RestApiGet()" is only possible in the master process | |
681 dicom = orthanc.RestApiGet('/instances/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5/file') | |
682 answer = POOL.apply(OffloadedDicomParsing, args = (dicom, )) | |
683 output.AnswerBuffer(answer, 'text/plain') | |
684 | |
685 Communication primitives such as ``multiprocessing.Queue`` are | |
686 available to exchange messages from the "slave" Python interpreters to | |
687 the "master" Python interpreter if further calls to the Orthanc SDK | |
688 are required. | |
689 | |
351 | 690 Obviously, an in-depth discussion about the ``multiprocessing`` |
691 library is out of the scope of this document. There are many | |
375 | 692 references available on Internet. Also, note that ``threading`` is not |
693 useful here, as Python multithreading is also limited by the GIL, and | |
694 is more targeted at dealing with costly I/O operations or with the | |
695 :ref:`scheduling of commands <python-scheduler>`. |