comparison OrthancServer/OrthancRestApi/OrthancRestModalities.cpp @ 751:5197fd35333c

refactoring of OrthancRestApi
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 14 Apr 2014 11:28:35 +0200
parents
children 3bd0589af992
comparison
equal deleted inserted replaced
750:4afad8cb94fd 751:5197fd35333c
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "OrthancRestApi.h"
34
35 #include "../DicomProtocol/DicomUserConnection.h"
36 #include "../OrthancInitialization.h"
37 #include "../../Core/HttpClient.h"
38
39 #include <glog/logging.h>
40
41 namespace Orthanc
42 {
43 // DICOM SCU ----------------------------------------------------------------
44
45 static bool MergeQueryAndTemplate(DicomMap& result,
46 const std::string& postData)
47 {
48 Json::Value query;
49 Json::Reader reader;
50
51 if (!reader.parse(postData, query) ||
52 query.type() != Json::objectValue)
53 {
54 return false;
55 }
56
57 Json::Value::Members members = query.getMemberNames();
58 for (size_t i = 0; i < members.size(); i++)
59 {
60 DicomTag t = FromDcmtkBridge::ParseTag(members[i]);
61 result.SetValue(t, query[members[i]].asString());
62 }
63
64 return true;
65 }
66
67 static void DicomFindPatient(RestApi::PostCall& call)
68 {
69 DicomMap m;
70 DicomMap::SetupFindPatientTemplate(m);
71 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
72 {
73 return;
74 }
75
76 DicomUserConnection connection;
77 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", ""));
78
79 DicomFindAnswers answers;
80 connection.FindPatient(answers, m);
81
82 Json::Value result;
83 answers.ToJson(result);
84 call.GetOutput().AnswerJson(result);
85 }
86
87 static void DicomFindStudy(RestApi::PostCall& call)
88 {
89 DicomMap m;
90 DicomMap::SetupFindStudyTemplate(m);
91 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
92 {
93 return;
94 }
95
96 if (m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
97 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2)
98 {
99 return;
100 }
101
102 DicomUserConnection connection;
103 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", ""));
104
105 DicomFindAnswers answers;
106 connection.FindStudy(answers, m);
107
108 Json::Value result;
109 answers.ToJson(result);
110 call.GetOutput().AnswerJson(result);
111 }
112
113 static void DicomFindSeries(RestApi::PostCall& call)
114 {
115 DicomMap m;
116 DicomMap::SetupFindSeriesTemplate(m);
117 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
118 {
119 return;
120 }
121
122 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
123 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
124 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2)
125 {
126 return;
127 }
128
129 DicomUserConnection connection;
130 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", ""));
131
132 DicomFindAnswers answers;
133 connection.FindSeries(answers, m);
134
135 Json::Value result;
136 answers.ToJson(result);
137 call.GetOutput().AnswerJson(result);
138 }
139
140 static void DicomFindInstance(RestApi::PostCall& call)
141 {
142 DicomMap m;
143 DicomMap::SetupFindInstanceTemplate(m);
144 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
145 {
146 return;
147 }
148
149 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
150 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
151 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 ||
152 m.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2)
153 {
154 return;
155 }
156
157 DicomUserConnection connection;
158 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", ""));
159
160 DicomFindAnswers answers;
161 connection.FindInstance(answers, m);
162
163 Json::Value result;
164 answers.ToJson(result);
165 call.GetOutput().AnswerJson(result);
166 }
167
168 static void DicomFind(RestApi::PostCall& call)
169 {
170 DicomMap m;
171 DicomMap::SetupFindPatientTemplate(m);
172 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
173 {
174 return;
175 }
176
177 DicomUserConnection connection;
178 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", ""));
179
180 DicomFindAnswers patients;
181 connection.FindPatient(patients, m);
182
183 // Loop over the found patients
184 Json::Value result = Json::arrayValue;
185 for (size_t i = 0; i < patients.GetSize(); i++)
186 {
187 Json::Value patient(Json::objectValue);
188 FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i));
189
190 DicomMap::SetupFindStudyTemplate(m);
191 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
192 {
193 return;
194 }
195 m.CopyTagIfExists(patients.GetAnswer(i), DICOM_TAG_PATIENT_ID);
196
197 DicomFindAnswers studies;
198 connection.FindStudy(studies, m);
199
200 patient["Studies"] = Json::arrayValue;
201
202 // Loop over the found studies
203 for (size_t j = 0; j < studies.GetSize(); j++)
204 {
205 Json::Value study(Json::objectValue);
206 FromDcmtkBridge::ToJson(study, studies.GetAnswer(j));
207
208 DicomMap::SetupFindSeriesTemplate(m);
209 if (!MergeQueryAndTemplate(m, call.GetPostBody()))
210 {
211 return;
212 }
213 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_PATIENT_ID);
214 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_STUDY_INSTANCE_UID);
215
216 DicomFindAnswers series;
217 connection.FindSeries(series, m);
218
219 // Loop over the found series
220 study["Series"] = Json::arrayValue;
221 for (size_t k = 0; k < series.GetSize(); k++)
222 {
223 Json::Value series2(Json::objectValue);
224 FromDcmtkBridge::ToJson(series2, series.GetAnswer(k));
225 study["Series"].append(series2);
226 }
227
228 patient["Studies"].append(study);
229 }
230
231 result.append(patient);
232 }
233
234 call.GetOutput().AnswerJson(result);
235 }
236
237
238 static bool GetInstancesToExport(std::list<std::string>& instances,
239 const std::string& remote,
240 RestApi::PostCall& call)
241 {
242 ServerContext& context = OrthancRestApi::GetContext(call);
243
244 std::string stripped = Toolbox::StripSpaces(call.GetPostBody());
245
246 Json::Value request;
247 if (Toolbox::IsSHA1(stripped))
248 {
249 // This is for compatibility with Orthanc <= 0.5.1.
250 request = stripped;
251 }
252 else if (!call.ParseJsonRequest(request))
253 {
254 // Bad JSON request
255 return false;
256 }
257
258 if (request.isString())
259 {
260 context.GetIndex().LogExportedResource(request.asString(), remote);
261 context.GetIndex().GetChildInstances(instances, request.asString());
262 }
263 else if (request.isArray())
264 {
265 for (Json::Value::ArrayIndex i = 0; i < request.size(); i++)
266 {
267 if (!request[i].isString())
268 {
269 return false;
270 }
271
272 std::string stripped = Toolbox::StripSpaces(request[i].asString());
273 if (!Toolbox::IsSHA1(stripped))
274 {
275 return false;
276 }
277
278 context.GetIndex().LogExportedResource(stripped, remote);
279
280 std::list<std::string> tmp;
281 context.GetIndex().GetChildInstances(tmp, stripped);
282
283 for (std::list<std::string>::const_iterator
284 it = tmp.begin(); it != tmp.end(); ++it)
285 {
286 instances.push_back(*it);
287 }
288 }
289 }
290 else
291 {
292 // Neither a string, nor a list of strings. Bad request.
293 return false;
294 }
295
296 return true;
297 }
298
299
300 static void DicomStore(RestApi::PostCall& call)
301 {
302 ServerContext& context = OrthancRestApi::GetContext(call);
303
304 std::string remote = call.GetUriComponent("id", "");
305
306 std::list<std::string> instances;
307 if (!GetInstancesToExport(instances, remote, call))
308 {
309 return;
310 }
311
312 DicomUserConnection connection;
313 ConnectToModalityUsingSymbolicName(connection, remote);
314
315 for (std::list<std::string>::const_iterator
316 it = instances.begin(); it != instances.end(); ++it)
317 {
318 LOG(INFO) << "Sending resource " << *it << " to modality \"" << remote << "\"";
319
320 std::string dicom;
321 context.ReadFile(dicom, *it, FileContentType_Dicom);
322 connection.Store(dicom);
323 }
324
325 call.GetOutput().AnswerBuffer("{}", "application/json");
326 }
327
328
329 // Orthanc Peers ------------------------------------------------------------
330
331 static bool IsExistingPeer(const OrthancRestApi::SetOfStrings& peers,
332 const std::string& id)
333 {
334 return peers.find(id) != peers.end();
335 }
336
337 static void ListPeers(RestApi::GetCall& call)
338 {
339 OrthancRestApi::SetOfStrings peers;
340 GetListOfOrthancPeers(peers);
341
342 Json::Value result = Json::arrayValue;
343 for (OrthancRestApi::SetOfStrings::const_iterator
344 it = peers.begin(); it != peers.end(); ++it)
345 {
346 result.append(*it);
347 }
348
349 call.GetOutput().AnswerJson(result);
350 }
351
352 static void ListPeerOperations(RestApi::GetCall& call)
353 {
354 OrthancRestApi::SetOfStrings peers;
355 GetListOfOrthancPeers(peers);
356
357 std::string id = call.GetUriComponent("id", "");
358 if (IsExistingPeer(peers, id))
359 {
360 Json::Value result = Json::arrayValue;
361 result.append("store");
362 call.GetOutput().AnswerJson(result);
363 }
364 }
365
366 static void PeerStore(RestApi::PostCall& call)
367 {
368 ServerContext& context = OrthancRestApi::GetContext(call);
369
370 std::string remote = call.GetUriComponent("id", "");
371
372 std::list<std::string> instances;
373 if (!GetInstancesToExport(instances, remote, call))
374 {
375 return;
376 }
377
378 std::string url, username, password;
379 GetOrthancPeer(remote, url, username, password);
380
381 // Configure the HTTP client
382 HttpClient client;
383 if (username.size() != 0 && password.size() != 0)
384 {
385 client.SetCredentials(username.c_str(), password.c_str());
386 }
387
388 client.SetUrl(url + "instances");
389 client.SetMethod(HttpMethod_Post);
390
391 // Loop over the instances that are to be sent
392 for (std::list<std::string>::const_iterator
393 it = instances.begin(); it != instances.end(); ++it)
394 {
395 LOG(INFO) << "Sending resource " << *it << " to peer \"" << remote << "\"";
396
397 context.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom);
398
399 std::string answer;
400 if (!client.Apply(answer))
401 {
402 LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << remote << "\"";
403 return;
404 }
405 }
406
407 call.GetOutput().AnswerBuffer("{}", "application/json");
408 }
409
410
411 // DICOM bridge -------------------------------------------------------------
412
413 static bool IsExistingModality(const OrthancRestApi::SetOfStrings& modalities,
414 const std::string& id)
415 {
416 return modalities.find(id) != modalities.end();
417 }
418
419 static void ListModalities(RestApi::GetCall& call)
420 {
421 OrthancRestApi::SetOfStrings modalities;
422 GetListOfDicomModalities(modalities);
423
424 Json::Value result = Json::arrayValue;
425 for (OrthancRestApi::SetOfStrings::const_iterator
426 it = modalities.begin(); it != modalities.end(); ++it)
427 {
428 result.append(*it);
429 }
430
431 call.GetOutput().AnswerJson(result);
432 }
433
434
435 static void ListModalityOperations(RestApi::GetCall& call)
436 {
437 OrthancRestApi::SetOfStrings modalities;
438 GetListOfDicomModalities(modalities);
439
440 std::string id = call.GetUriComponent("id", "");
441 if (IsExistingModality(modalities, id))
442 {
443 Json::Value result = Json::arrayValue;
444 result.append("find-patient");
445 result.append("find-study");
446 result.append("find-series");
447 result.append("find-instance");
448 result.append("find");
449 result.append("store");
450 call.GetOutput().AnswerJson(result);
451 }
452 }
453
454
455 void OrthancRestApi::RegisterModalities()
456 {
457 Register("/modalities", ListModalities);
458 Register("/modalities/{id}", ListModalityOperations);
459 Register("/modalities/{id}/find-patient", DicomFindPatient);
460 Register("/modalities/{id}/find-study", DicomFindStudy);
461 Register("/modalities/{id}/find-series", DicomFindSeries);
462 Register("/modalities/{id}/find-instance", DicomFindInstance);
463 Register("/modalities/{id}/find", DicomFind);
464 Register("/modalities/{id}/store", DicomStore);
465
466 Register("/peers", ListPeers);
467 Register("/peers/{id}", ListPeerOperations);
468 Register("/peers/{id}/store", PeerStore);
469 }
470 }