Mercurial > hg > orthanc-book
annotate Sphinx/source/plugins/python.rst @ 702:6e02cd89eb6a
moving python samples in separate files
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 11 Jun 2021 09:38:15 +0200 |
parents | f093160dd7f4 |
children | a589668768d7 |
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 | 21 <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.9.3/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h>`__ |
345 | 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 | 25 As of release 2.0 of the plugin, the coverage of the C SDK is about |
26 75% (119 functions are automatically wrapped in Python out of a total | |
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 | 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 |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
69 .. highlight:: python |
364
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 | 111 `Here <https://bitbucket.org/osimis/orthanc-setup-samples/src/master/docker/python/>`__ is a full example |
112 of a more complex setup using the :ref:`osimis/orthanc <docker-osimis>` images. | |
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 | 254 |
255 Listening to changes | |
256 .................... | |
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 | 260 This sample uploads a DICOM file as soon as Orthanc is started:: |
261 | |
262 import orthanc | |
695
f4fc12ed00ee
creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
678
diff
changeset
|
263 |
345 | 264 def OnChange(changeType, level, resource): |
265 if changeType == orthanc.ChangeType.ORTHANC_STARTED: | |
266 print('Started') | |
695
f4fc12ed00ee
creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
678
diff
changeset
|
267 |
345 | 268 with open('/tmp/sample.dcm', 'rb') as f: |
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 | 271 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: |
272 print('Stopped') | |
695
f4fc12ed00ee
creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
678
diff
changeset
|
273 |
345 | 274 elif changeType == orthanc.ChangeType.NEW_INSTANCE: |
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 | 277 orthanc.RegisterOnChangeCallback(OnChange) |
278 | |
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 | 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 | 320 |
321 :: | |
322 | |
323 import orthanc | |
324 import json | |
325 import pprint | |
326 | |
327 def OnStoredInstance(dicom, instanceId): | |
328 print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % ( | |
329 instanceId, dicom.GetInstanceSize(), | |
330 dicom.GetInstanceMetadata('TransferSyntax'), | |
331 dicom.GetInstanceMetadata('SopClassUid'))) | |
332 | |
333 # Print the origin information | |
334 if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL: | |
335 print('This instance was received through the DICOM protocol') | |
336 elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API: | |
337 print('This instance was received through the REST API') | |
338 | |
339 # Print the DICOM tags | |
340 pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson())) | |
341 | |
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 | 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 | 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 | 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 | 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 | 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 | 391 |
392 Auto-routing studies | |
393 .................... | |
394 | |
395 .. highlight:: python | |
396 | |
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 | 399 ``DicomModalities`` configuration option):: |
400 | |
401 import orthanc | |
402 | |
403 def OnChange(changeType, level, resourceId): | |
404 if changeType == orthanc.ChangeType.STABLE_STUDY: | |
405 print('Stable study: %s' % resourceId) | |
406 orthanc.RestApiPost('/modalities/sample/store', resourceId) | |
407 | |
408 orthanc.RegisterOnChangeCallback(OnChange) | |
348 | 409 |
410 | |
539 | 411 Note that, if you want to use an orthanc plugin to transfer the study, |
412 you should use the ``RestApiPostAfterPlugins()`` method:: | |
413 | |
414 import orthanc | |
415 | |
416 def OnChange(changeType, level, resourceId): | |
417 if changeType == orthanc.ChangeType.STABLE_STUDY: | |
418 print('Stable study: %s' % resourceId) | |
419 orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId) | |
420 | |
421 orthanc.RegisterOnChangeCallback(OnChange) | |
422 | |
423 | |
352 | 424 Rendering a thumbnail using PIL/Pillow |
425 ...................................... | |
348 | 426 |
427 .. highlight:: python | |
428 | |
429 :: | |
430 | |
431 from PIL import Image | |
432 import io | |
433 import orthanc | |
434 | |
435 def DecodeInstance(output, uri, **request): | |
436 if request['method'] == 'GET': | |
437 # Retrieve the instance ID from the regular expression (*) | |
438 instanceId = request['groups'][0] | |
439 | |
440 # Render the instance, then open it in Python using PIL/Pillow | |
441 png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId) | |
442 image = Image.open(io.BytesIO(png)) | |
443 | |
444 # Downsize the image as a 64x64 thumbnail | |
445 image.thumbnail((64, 64), Image.ANTIALIAS) | |
446 | |
447 # Save the thumbnail as JPEG, then send the buffer to the caller | |
448 jpeg = io.BytesIO() | |
449 image.save(jpeg, format = "JPEG", quality = 80) | |
450 jpeg.seek(0) | |
451 output.AnswerBuffer(jpeg.read(), 'text/plain') | |
452 | |
453 else: | |
454 output.SendMethodNotAllowed('GET') | |
455 | |
456 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) | |
351 | 457 |
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 |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
763 Forbid or allow access to REST resources (authorization, new in 3.0) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
764 .................................................................... |
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
|
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 | 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 |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
872 .. _python_dicom_scp: |
695
f4fc12ed00ee
creating dicom instances in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
678
diff
changeset
|
873 |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
874 Handling DICOM SCP requests (new in 3.2) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
875 ........................................ |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
876 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
877 .. highlight:: python |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
878 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
879 Starting with release 3.2 of the Python plugin, it is possible to |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
880 replace the C-FIND SCP and C-MOVE SCP of Orthanc by a Python |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
881 script. This feature can notably be used to create a custom DICOM |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
882 proxy. Here is a minimal example:: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
883 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
884 import json |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
885 import orthanc |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
886 import pprint |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
887 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
888 def OnFind(answers, query, issuerAet, calledAet): |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
889 print('Received incoming C-FIND request from %s:' % issuerAet) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
890 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
891 answer = {} |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
892 for i in range(query.GetFindQuerySize()): |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
893 print(' %s (%04x,%04x) = [%s]' % (query.GetFindQueryTagName(i), |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
894 query.GetFindQueryTagGroup(i), |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
895 query.GetFindQueryTagElement(i), |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
896 query.GetFindQueryValue(i))) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
897 answer[query.GetFindQueryTagName(i)] = ('HELLO%d-%s' % (i, query.GetFindQueryValue(i))) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
898 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
899 answers.FindAddAnswer(orthanc.CreateDicom( |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
900 json.dumps(answer), None, orthanc.CreateDicomFlags.NONE)) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
901 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
902 def OnMove(**request): |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
903 orthanc.LogWarning('C-MOVE request to be handled in Python: %s' % |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
904 json.dumps(request, indent = 4, sort_keys = True)) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
905 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
906 orthanc.RegisterFindCallback(OnFind) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
907 orthanc.RegisterMoveCallback(OnMove) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
908 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
909 .. highlight:: text |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
910 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
911 In this sample, the C-FIND SCP will send one single answer that |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
912 reproduces the values provided by the SCU:: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
913 |
699 | 914 $ findscu localhost 4242 -S -k QueryRetrieveLevel=STUDY -k PatientName=TEST -k SeriesDescription= |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
915 I: --------------------------- |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
916 I: Find Response: 1 (Pending) |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
917 I: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
918 I: # Dicom-Data-Set |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
919 I: # Used TransferSyntax: Little Endian Explicit |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
920 I: (0008,0005) CS [ISO_IR 100] # 10, 1 SpecificCharacterSet |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
921 I: (0008,0052) CS [HELLO0-STUDY] # 12, 1 QueryRetrieveLevel |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
922 I: (0008,103e) LO [HELLO1- ] # 8, 1 SeriesDescription |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
923 I: (0010,0010) PN [HELLO2-TEST ] # 12, 1 PatientName |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
924 I: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
925 |
698 | 926 A more realistic Python script could for instance call the route |
927 ``/modalities/{...}/query`` in the :ref:`REST API <rest-find-scu>` of | |
928 Orthanc using ``orthanc.RestApiPost()``, in order to query the content | |
929 a remote modality through a second C-FIND SCU request (this time | |
930 issued by Orthanc as a SCU). | |
931 | |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
932 The C-MOVE SCP can be invoked as follows:: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
933 |
699 | 934 $ movescu localhost 4242 -aem TARGET -aec SOURCE -aet MOVESCU -S -k QueryRetrieveLevel=IMAGE -k StudyInstanceUID=1.2.3.4 |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
935 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
936 The C-MOVE request above would print the following information in the |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
937 Orthanc logs:: |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
938 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
939 W0610 18:30:36.840865 PluginsManager.cpp:168] C-MOVE request to be handled in Python: { |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
940 "AccessionNumber": "", |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
941 "Level": "INSTANCE", |
699 | 942 "OriginatorAET": "MOVESCU", |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
943 "OriginatorID": 1, |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
944 "PatientID": "", |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
945 "SOPInstanceUID": "", |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
946 "SeriesInstanceUID": "", |
699 | 947 "SourceAET": "SOURCE", |
697
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
948 "StudyInstanceUID": "1.2.3.4", |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
949 "TargetAET": "TARGET" |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
950 } |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
951 |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
952 It is now up to your Python callback to proces the C-MOVE SCU request, |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
953 for instance by calling the route ``/modalities/{...}/store`` in the |
7581dc208323
Handling DICOM SCP requests using a Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
696
diff
changeset
|
954 :ref:`REST API <rest-store-scu>` of Orthanc using |
700
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
955 ``orthanc.RestApiPost()``. It is highly advised to create a Python |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
956 thread to handle the request, in order to avoid blocking Orthanc as |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
957 much as possible. |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
958 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
959 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
960 .. _python_worklists: |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
961 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
962 Handling worklist SCP requests (new in 3.2) |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
963 ........................................... |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
964 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
965 Starting with release 3.2 of the Python plugin, it is possible to |
701
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
966 answer :ref:`worklist queries <worklist>` using a Python script. This |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
967 is especially useful to easily create a bridge between Orthanc, |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
968 HL7/FHIR messages and RIS systems. Indeed, Python provides many tools |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
969 to handle HL7 such as `python-hl7 library |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
970 <https://python-hl7.readthedocs.io/en/latest/>`__. |
700
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
971 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
972 The following Python script reproduces features similar to the |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
973 :ref:`sample modality worklists plugin <worklists-plugin>`: |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
974 |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
975 .. literalinclude:: python/worklist.py |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
976 :language: python |
56a06ca9ec20
Handling worklist SCP requests in Python
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
699
diff
changeset
|
977 |
701
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
978 .. highlight:: text |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
979 |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
980 Here is the result of this plugin on a sample call:: |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
981 |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
982 $ findscu -W -k "ScheduledProcedureStepSequence[0].Modality=MR" 127.0.0.1 4242 |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
983 I: --------------------------- |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
984 I: Find Response: 1 (Pending) |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
985 I: |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
986 I: # Dicom-Data-Set |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
987 I: # Used TransferSyntax: Little Endian Explicit |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
988 I: (0008,0005) CS [ISO_IR 100] # 10, 1 SpecificCharacterSet |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
989 I: (0040,0100) SQ (Sequence with explicit length #=1) # 18, 1 ScheduledProcedureStepSequence |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
990 I: (fffe,e000) na (Item with explicit length #=1) # 10, 1 Item |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
991 I: (0008,0060) CS [MR] # 2, 1 Modality |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
992 I: (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
993 I: (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
994 I: |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
995 I: --------------------------- |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
996 I: Find Response: 2 (Pending) |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
997 I: |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
998 I: # Dicom-Data-Set |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
999 I: # Used TransferSyntax: Little Endian Explicit |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1000 I: (0008,0005) CS [ISO_IR 100] # 10, 1 SpecificCharacterSet |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1001 I: (0040,0100) SQ (Sequence with explicit length #=1) # 18, 1 ScheduledProcedureStepSequence |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1002 I: (fffe,e000) na (Item with explicit length #=1) # 10, 1 Item |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1003 I: (0008,0060) CS [MR] # 2, 1 Modality |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1004 I: (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1005 I: (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1006 I: |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1007 |
f093160dd7f4
cross-references regarding worklists
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
700
diff
changeset
|
1008 |
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
|
1009 |
351 | 1010 Performance and concurrency |
1011 --------------------------- | |
1012 | |
471
a735476a0e6e
note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
462
diff
changeset
|
1013 **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
|
1014 ``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
|
1015 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
|
1016 <https://en.wikipedia.org/wiki/Fork_(system_call)>`__. |
a735476a0e6e
note about fork on windows
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
462
diff
changeset
|
1017 |
555
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1018 Using slave processes |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1019 ..................... |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1020 |
351 | 1021 Let us consider the following sample Python script that makes a |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1022 CPU-intensive computation on a REST callback: |
351 | 1023 |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1024 .. literalinclude:: python/multiprocessing-1.py |
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1025 :language: python |
351 | 1026 |
1027 .. highlight:: text | |
1028 | |
1029 Calling this REST route from the command-line returns the time that is | |
1030 needed to compute 30 million times a squared root on your CPU:: | |
1031 | |
1032 $ curl http://localhost:8042/computation | |
1033 computation done in 4.208 seconds | |
1034 | |
1035 Now, let us call this route three times concurrently (we use bash):: | |
1036 | |
1037 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation ) | |
1038 computation done in 11.262 seconds | |
1039 computation done in 12.457 seconds | |
1040 computation done in 13.360 seconds | |
1041 | |
1042 As can be seen, the computation time has tripled. This means that the | |
1043 computations were not distributed across the available CPU cores. | |
1044 This might seem surprising, as Orthanc is a threaded server (in | |
1045 Orthanc, a pool of C++ threads serves concurrent requests). | |
1046 | |
1047 The explanation is that the Python interpreter (`CPython | |
1048 <https://en.wikipedia.org/wiki/CPython>`__ actually) is built on the | |
1049 top of a so-called `Global Interpreter Lock (GIL) | |
1050 <https://en.wikipedia.org/wiki/Global_interpreter_lock>`__. The GIL is | |
1051 basically a mutex that protects all the calls to the Python | |
1052 interpreter. If multiple C++ threads from Orthanc call a Python | |
353 | 1053 callback, only one can proceed at any given time. Note however that |
1054 the GIL only applies to the Python script: The baseline REST API of | |
1055 Orthanc is not affected by the GIL. | |
351 | 1056 |
1057 The solution is to use the `multiprocessing primitives | |
1058 <https://docs.python.org/3/library/multiprocessing.html>`__ of Python. | |
1059 The "master" Python interpreter that is initially started by the | |
1060 Orthanc plugin, can start several `children processes | |
1061 <https://en.wikipedia.org/wiki/Process_(computing)>`__, each of these | |
1062 processes running a separate Python interpreter. This allows to | |
1063 offload intensive computations from the "master" Python interpreter of | |
1064 Orthanc onto those "slave" interpreters. The ``multiprocessing`` | |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1065 library is actually quite straightforward to use: |
351 | 1066 |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1067 .. literalinclude:: python/multiprocessing-2.py |
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1068 :language: python |
351 | 1069 |
1070 .. highlight:: text | |
1071 | |
1072 Here is now the result of calling this route three times concurrently:: | |
1073 | |
1074 $ (curl http://localhost:8042/computation & curl http://localhost:8042/computation & curl http://localhost:8042/computation ) | |
1075 computation done in 4.211 seconds | |
1076 computation done in 4.215 seconds | |
1077 computation done in 4.225 seconds | |
1078 | |
1079 As can be seen, the calls to the Python computation now fully run in | |
1080 parallel (the time is cut down from 12 seconds to 4 seconds, the same | |
1081 as for one isolated request). | |
1082 | |
1083 Note also how the ``multiprocessing`` library allows to make a fine | |
1084 control over the computational resources that are available to the | |
1085 Python script: The number of "slave" interpreters can be easily | |
1086 changed in the constructor of the ``multiprocessing.Pool`` object, and | |
1087 are fully independent of the threads used by the Orthanc server. | |
1088 | |
555
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1089 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
|
1090 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
|
1091 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
|
1092 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
|
1093 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
|
1094 :ref:`scheduling of commands <python-scheduler>`. |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1095 |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1096 |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1097 Slave processes and the "orthanc" module |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1098 ........................................ |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1099 |
352 | 1100 .. highlight:: python |
1101 | |
555
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1102 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
|
1103 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
|
1104 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
|
1105 |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1106 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
|
1107 ``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
|
1108 process. For instance, here is how you would parse a DICOM file in a |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1109 slave process: |
352 | 1110 |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1111 .. literalinclude:: python/multiprocessing-3.py |
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1112 :language: python |
352 | 1113 |
1114 Communication primitives such as ``multiprocessing.Queue`` are | |
1115 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
|
1116 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
|
1117 |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1118 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
|
1119 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
|
1120 way. The function ``orthanc.GenerateRestApiAuthorizationToken()`` can |
6fb469a3c382
Python plugin: documentation of orthanc.GenerateRestApiAuthorizationToken()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
554
diff
changeset
|
1121 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
|
1122 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
|
1123 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
|
1124 <https://requests.readthedocs.io/en/master/>`__, can then be used to |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1125 access the REST API of Orthanc. Here is a minimal example: |
352 | 1126 |
702
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1127 .. literalinclude:: python/multiprocessing-4.py |
6e02cd89eb6a
moving python samples in separate files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
701
diff
changeset
|
1128 :language: python |