Mercurial > hg > orthanc-tests
annotate Tests/Toolbox.py @ 179:8a2dd77d4035
testing split/merge
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 01 Oct 2018 17:54:32 +0200 |
parents | d468cbe1b161 |
children | 911070f790e3 |
rev | line source |
---|---|
0 | 1 #!/usr/bin/python |
2 | |
1 | 3 # Orthanc - A Lightweight, RESTful DICOM Store |
73 | 4 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
1 | 5 # Department, University Hospital of Liege, Belgium |
130
50cd127e5330
upgrade to year 2018
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
100
diff
changeset
|
6 # Copyright (C) 2017-2018 Osimis S.A., Belgium |
1 | 7 # |
8 # This program is free software: you can redistribute it and/or | |
9 # modify it under the terms of the GNU General Public License as | |
10 # published by the Free Software Foundation, either version 3 of the | |
11 # License, or (at your option) any later version. | |
12 # | |
13 # This program is distributed in the hope that it will be useful, but | |
14 # WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 # General Public License for more details. | |
17 # | |
18 # You should have received a copy of the GNU General Public License | |
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
0 | 20 |
21 | |
22 import hashlib | |
23 import httplib2 | |
24 import json | |
4 | 25 import os |
1 | 26 import re |
4 | 27 import signal |
1 | 28 import subprocess |
4 | 29 import threading |
83 | 30 import sys |
0 | 31 import time |
1 | 32 import zipfile |
0 | 33 |
3 | 34 from PIL import Image |
83 | 35 |
36 if (sys.version_info >= (3, 0)): | |
37 from urllib.parse import urlencode | |
38 from io import StringIO | |
39 from io import BytesIO | |
40 | |
41 else: | |
42 from urllib import urlencode | |
43 | |
44 # http://stackoverflow.com/a/1313868/881731 | |
45 try: | |
46 from cStringIO import StringIO | |
47 except: | |
48 from StringIO import StringIO | |
3 | 49 |
0 | 50 |
83 | 51 def _DecodeJson(s): |
52 t = s | |
53 | |
54 if (sys.version_info >= (3, 0)): | |
55 try: | |
56 t = s.decode() | |
57 except: | |
58 pass | |
59 | |
60 try: | |
61 return json.loads(t) | |
62 except: | |
63 return t | |
0 | 64 |
65 | |
13 | 66 def DefineOrthanc(server = 'localhost', |
67 restPort = 8042, | |
1 | 68 username = None, |
69 password = None, | |
70 aet = 'ORTHANC', | |
71 dicomPort = 4242): | |
13 | 72 #m = re.match(r'(http|https)://([^:]+):([^@]+)@([^@]+)', url) |
73 #if m != None: | |
74 # url = m.groups()[0] + '://' + m.groups()[3] | |
75 # username = m.groups()[1] | |
76 # password = m.groups()[2] | |
0 | 77 |
13 | 78 #if not url.endswith('/'): |
79 # url += '/' | |
0 | 80 |
1 | 81 return { |
13 | 82 'Server' : server, |
83 'Url' : 'http://%s:%d/' % (server, restPort), | |
1 | 84 'Username' : username, |
85 'Password' : password, | |
86 'DicomAet' : aet, | |
87 'DicomPort' : dicomPort | |
88 } | |
0 | 89 |
90 | |
91 def _SetupCredentials(orthanc, http): | |
1 | 92 if (orthanc['Username'] != None and |
93 orthanc['Password'] != None): | |
94 http.add_credentials(orthanc['Username'], orthanc['Password']) | |
0 | 95 |
28 | 96 def DoGetRaw(orthanc, uri, data = {}, body = None, headers = {}): |
0 | 97 d = '' |
98 if len(data.keys()) > 0: | |
99 d = '?' + urlencode(data) | |
100 | |
101 http = httplib2.Http() | |
21
2a29bcff60a7
tests of image decoding
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
13
diff
changeset
|
102 http.follow_redirects = False |
0 | 103 _SetupCredentials(orthanc, http) |
104 | |
1 | 105 resp, content = http.request(orthanc['Url'] + uri + d, 'GET', body = body, |
0 | 106 headers = headers) |
28 | 107 return (resp, content) |
108 | |
109 | |
110 def DoGet(orthanc, uri, data = {}, body = None, headers = {}): | |
111 (resp, content) = DoGetRaw(orthanc, uri, data = data, body = body, headers = headers) | |
112 | |
0 | 113 if not (resp.status in [ 200 ]): |
114 raise Exception(resp.status) | |
115 else: | |
83 | 116 return _DecodeJson(content) |
0 | 117 |
118 def _DoPutOrPost(orthanc, uri, method, data, contentType, headers): | |
119 http = httplib2.Http() | |
21
2a29bcff60a7
tests of image decoding
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
13
diff
changeset
|
120 http.follow_redirects = False |
0 | 121 _SetupCredentials(orthanc, http) |
122 | |
83 | 123 if isinstance(data, (str, bytearray, bytes)): |
0 | 124 body = data |
125 if len(contentType) != 0: | |
126 headers['content-type'] = contentType | |
127 else: | |
128 body = json.dumps(data) | |
129 headers['content-type'] = 'application/json' | |
130 | |
131 headers['expect'] = '' | |
132 | |
1 | 133 resp, content = http.request(orthanc['Url'] + uri, method, |
0 | 134 body = body, |
135 headers = headers) | |
136 if not (resp.status in [ 200, 302 ]): | |
137 raise Exception(resp.status) | |
138 else: | |
83 | 139 return _DecodeJson(content) |
0 | 140 |
141 def DoDelete(orthanc, uri): | |
142 http = httplib2.Http() | |
21
2a29bcff60a7
tests of image decoding
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
13
diff
changeset
|
143 http.follow_redirects = False |
0 | 144 _SetupCredentials(orthanc, http) |
145 | |
1 | 146 resp, content = http.request(orthanc['Url'] + uri, 'DELETE') |
0 | 147 if not (resp.status in [ 200 ]): |
148 raise Exception(resp.status) | |
149 else: | |
83 | 150 return _DecodeJson(content) |
0 | 151 |
152 def DoPut(orthanc, uri, data = {}, contentType = ''): | |
10 | 153 return _DoPutOrPost(orthanc, uri, 'PUT', data, contentType, {}) |
0 | 154 |
155 def DoPost(orthanc, uri, data = {}, contentType = '', headers = {}): | |
156 return _DoPutOrPost(orthanc, uri, 'POST', data, contentType, headers) | |
157 | |
13 | 158 def GetDatabasePath(filename): |
159 return os.path.join(os.path.dirname(__file__), '..', 'Database', filename) | |
160 | |
0 | 161 def UploadInstance(orthanc, filename): |
83 | 162 with open(GetDatabasePath(filename), 'rb') as f: |
163 d = f.read() | |
164 | |
0 | 165 return DoPost(orthanc, '/instances', d, 'application/dicom') |
166 | |
167 def UploadFolder(orthanc, path): | |
13 | 168 for i in os.listdir(GetDatabasePath(path)): |
1 | 169 try: |
170 UploadInstance(orthanc, os.path.join(path, i)) | |
171 except: | |
172 pass | |
0 | 173 |
174 def DropOrthanc(orthanc): | |
175 # Reset the Lua callbacks | |
176 DoPost(orthanc, '/tools/execute-script', 'function OnStoredInstance(instanceId, tags, metadata) end', 'application/lua') | |
177 | |
178 DoDelete(orthanc, '/exports') | |
179 | |
180 for s in DoGet(orthanc, '/patients'): | |
181 DoDelete(orthanc, '/patients/%s' % s) | |
182 | |
174 | 183 def InstallLuaScriptFromPath(orthanc, path): |
184 with open(GetDatabasePath(path), 'r') as f: | |
185 InstallLuaScript(orthanc, f.read()) | |
186 | |
187 def InstallLuaScript(orthanc, script): | |
188 DoPost(orthanc, '/tools/execute-script', script, 'application/lua') | |
189 | |
190 def UninstallLuaCallbacks(orthanc): | |
191 DoPost(orthanc, '/tools/execute-script', 'function OnStoredInstance() end', 'application/lua') | |
192 InstallLuaScriptFromPath(orthanc, 'Lua/TransferSyntaxEnable.lua') | |
193 | |
194 | |
0 | 195 def ComputeMD5(data): |
196 m = hashlib.md5() | |
197 m.update(data) | |
198 return m.hexdigest() | |
199 | |
59
84378ada15ab
test_decode_brainix_as_jpeg
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
44
diff
changeset
|
200 def GetImage(orthanc, uri, headers = {}): |
0 | 201 # http://www.pythonware.com/library/pil/handbook/introduction.htm |
59
84378ada15ab
test_decode_brainix_as_jpeg
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
44
diff
changeset
|
202 data = DoGet(orthanc, uri, headers = headers) |
83 | 203 if (sys.version_info >= (3, 0)): |
204 return Image.open(BytesIO(data)) | |
205 else: | |
206 return Image.open(StringIO(data)) | |
0 | 207 |
208 def GetArchive(orthanc, uri): | |
209 # http://stackoverflow.com/a/1313868/881731 | |
210 s = DoGet(orthanc, uri) | |
83 | 211 |
212 if (sys.version_info >= (3, 0)): | |
213 return zipfile.ZipFile(BytesIO(s), "r") | |
214 else: | |
215 return zipfile.ZipFile(StringIO(s), "r") | |
0 | 216 |
1 | 217 def IsDefinedInLua(orthanc, name): |
0 | 218 s = DoPost(orthanc, '/tools/execute-script', 'print(type(%s))' % name, 'application/lua') |
219 return (s.strip() != 'nil') | |
220 | |
1 | 221 def WaitEmpty(orthanc): |
0 | 222 while True: |
1 | 223 if len(DoGet(orthanc, '/instances')) == 0: |
0 | 224 return |
225 time.sleep(0.1) | |
226 | |
137
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
227 def WaitJobDone(orthanc, job): |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
228 while True: |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
229 s = DoGet(orthanc, '/jobs/%s' % job) ['State'] |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
230 |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
231 if s == 'Success': |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
232 return True |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
233 elif s == 'Failure': |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
234 return False |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
235 |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
236 time.sleep(0.1) |
412d5f70447e
testing asynchronous c-move
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
130
diff
changeset
|
237 |
138 | 238 def MonitorJob(orthanc, func): # "func" is a lambda |
239 a = set(DoGet(orthanc, '/jobs')) | |
240 func() | |
241 b = set(DoGet(orthanc, '/jobs')) | |
242 | |
243 diff = list(b - a) | |
244 if len(diff) != 1: | |
245 print('No job was created!') | |
246 return False | |
247 else: | |
248 return WaitJobDone(orthanc, diff[0]) | |
249 | |
179
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
250 def MonitorJob2(orthanc, func): # "func" is a lambda |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
251 a = set(DoGet(orthanc, '/jobs')) |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
252 func() |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
253 b = set(DoGet(orthanc, '/jobs')) |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
254 |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
255 diff = list(b - a) |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
256 if len(diff) != 1: |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
257 print('No job was created!') |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
258 return None |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
259 elif WaitJobDone(orthanc, diff[0]): |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
260 return diff[0] |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
261 else: |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
262 print('Error while executing the job') |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
263 return None |
8a2dd77d4035
testing split/merge
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
174
diff
changeset
|
264 |
1 | 265 def GetDockerHostAddress(): |
266 route = subprocess.check_output([ '/sbin/ip', 'route' ]) | |
267 m = re.search(r'default via ([0-9.]+)', route) | |
268 if m == None: | |
269 return 'localhost' | |
270 else: | |
271 return m.groups()[0] | |
4 | 272 |
44
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
273 def FindExecutable(name): |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
274 p = os.path.join('/usr/local/bin', name) |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
275 if os.path.isfile(p): |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
276 return p |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
277 |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
278 p = os.path.join('/usr/local/sbin', name) |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
279 if os.path.isfile(p): |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
280 return p |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
281 |
ffa542cce638
Toolbox.FindExecutable()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
28
diff
changeset
|
282 return name |
4 | 283 |
173 | 284 def IsOrthancVersionAbove(orthanc, major, minor, revision): |
285 v = DoGet(orthanc, '/system')['Version'] | |
286 | |
287 if v == 'mainline': | |
288 return True | |
289 else: | |
290 tmp = v.split('.') | |
291 a = int(tmp[0]) | |
292 b = int(tmp[1]) | |
293 c = int(tmp[2]) | |
294 return (a > major or | |
295 (a == major and b > minor) or | |
296 (a == major and b == minor and c >= revision)) | |
4 | 297 |
298 | |
299 class ExternalCommandThread: | |
300 @staticmethod | |
301 def ExternalCommandFunction(arg, stop_event, command, env): | |
302 external = subprocess.Popen(command, env = env) | |
303 | |
304 while (not stop_event.is_set()): | |
305 error = external.poll() | |
306 if error != None: | |
307 # http://stackoverflow.com/a/1489838/881731 | |
308 os._exit(-1) | |
309 stop_event.wait(0.1) | |
310 | |
83 | 311 print('Stopping the external command') |
4 | 312 external.terminate() |
9 | 313 external.communicate() # Wait for the command to stop |
4 | 314 |
315 def __init__(self, command, env = None): | |
316 self.thread_stop = threading.Event() | |
317 self.thread = threading.Thread(target = self.ExternalCommandFunction, | |
318 args = (10, self.thread_stop, command, env)) | |
9 | 319 #self.daemon = True |
4 | 320 self.thread.start() |
321 | |
322 def stop(self): | |
323 self.thread_stop.set() | |
324 self.thread.join() |