Mercurial > hg > orthanc
annotate OrthancServer/OrthancRestApi.cpp @ 85:ebce15865cce
relative redirection
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 26 Sep 2012 09:26:50 +0200 |
parents | 6212bf978584 |
children | 2d96cb181f45 52c7eb60bbc8 |
rev | line source |
---|---|
0 | 1 /** |
62 | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
0 | 3 * Copyright (C) 2012 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 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 **/ | |
19 | |
20 | |
62 | 21 #include "OrthancRestApi.h" |
0 | 22 |
62 | 23 #include "OrthancInitialization.h" |
0 | 24 #include "FromDcmtkBridge.h" |
25 #include "../Core/Uuid.h" | |
26 | |
27 #include <dcmtk/dcmdata/dcistrmb.h> | |
28 #include <dcmtk/dcmdata/dcfilefo.h> | |
29 #include <boost/lexical_cast.hpp> | |
30 | |
62 | 31 namespace Orthanc |
0 | 32 { |
33 static void SendJson(HttpOutput& output, | |
34 const Json::Value& value) | |
35 { | |
36 Json::StyledWriter writer; | |
37 std::string s = writer.write(value); | |
38 output.AnswerBufferWithContentType(s, "application/json"); | |
39 } | |
40 | |
35
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
41 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
42 static void SimplifyTagsRecursion(Json::Value& target, |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
43 const Json::Value& source) |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
44 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
45 assert(source.isObject()); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
46 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
47 target = Json::objectValue; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
48 Json::Value::Members members = source.getMemberNames(); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
49 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
50 for (size_t i = 0; i < members.size(); i++) |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
51 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
52 const Json::Value& v = source[members[i]]; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
53 const std::string& name = v["Name"].asString(); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
54 const std::string& type = v["Type"].asString(); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
55 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
56 if (type == "String") |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
57 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
58 target[name] = v["Value"].asString(); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
59 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
60 else if (type == "TooLong" || |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
61 type == "Null") |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
62 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
63 target[name] = Json::nullValue; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
64 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
65 else if (type == "Sequence") |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
66 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
67 const Json::Value& array = v["Value"]; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
68 assert(array.isArray()); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
69 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
70 Json::Value children = Json::arrayValue; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
71 for (size_t i = 0; i < array.size(); i++) |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
72 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
73 Json::Value c; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
74 SimplifyTagsRecursion(c, array[i]); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
75 children.append(c); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
76 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
77 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
78 target[name] = children; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
79 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
80 else |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
81 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
82 assert(0); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
83 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
84 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
85 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
86 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
87 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
88 static void SimplifyTags(Json::Value& target, |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
89 const FileStorage& storage, |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
90 const std::string& fileUuid) |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
91 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
92 std::string s; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
93 storage.ReadFile(s, fileUuid); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
94 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
95 Json::Value source; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
96 Json::Reader reader; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
97 if (!reader.parse(s, source)) |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
98 { |
62 | 99 throw OrthancException("Corrupted JSON file"); |
35
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
100 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
101 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
102 SimplifyTagsRecursion(target, source); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
103 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
104 |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
105 |
62 | 106 bool OrthancRestApi::Store(Json::Value& result, |
50 | 107 const std::string& postData) |
0 | 108 { |
109 // Prepare an input stream for the memory buffer | |
110 DcmInputBufferStream is; | |
111 if (postData.size() > 0) | |
112 { | |
113 is.setBuffer(&postData[0], postData.size()); | |
114 } | |
115 is.setEos(); | |
116 | |
34
96e57b863dd9
option to disallow remote access
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
8
diff
changeset
|
117 //printf("[%d]\n", postData.size()); |
0 | 118 |
119 DcmFileFormat dicomFile; | |
120 if (dicomFile.read(is).good()) | |
121 { | |
122 DicomMap dicomSummary; | |
123 FromDcmtkBridge::Convert(dicomSummary, *dicomFile.getDataset()); | |
124 | |
125 Json::Value dicomJson; | |
126 FromDcmtkBridge::ToJson(dicomJson, *dicomFile.getDataset()); | |
127 | |
128 std::string instanceUuid; | |
129 StoreStatus status = StoreStatus_Failure; | |
130 if (postData.size() > 0) | |
131 { | |
132 status = index_.Store | |
133 (instanceUuid, storage_, reinterpret_cast<const char*>(&postData[0]), | |
134 postData.size(), dicomSummary, dicomJson, ""); | |
135 } | |
136 | |
137 switch (status) | |
138 { | |
139 case StoreStatus_Success: | |
140 result["ID"] = instanceUuid; | |
141 result["Path"] = "/instances/" + instanceUuid; | |
142 result["Status"] = "Success"; | |
143 return true; | |
144 | |
145 case StoreStatus_AlreadyStored: | |
146 result["ID"] = instanceUuid; | |
147 result["Path"] = "/instances/" + instanceUuid; | |
148 result["Status"] = "AlreadyStored"; | |
149 return true; | |
150 | |
151 default: | |
152 return false; | |
153 } | |
154 } | |
155 | |
156 return false; | |
157 } | |
158 | |
62 | 159 void OrthancRestApi::ConnectToModality(DicomUserConnection& c, |
50 | 160 const std::string& name) |
0 | 161 { |
162 std::string aet, address; | |
163 int port; | |
164 GetDicomModality(name, aet, address, port); | |
62 | 165 c.SetLocalApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); |
0 | 166 c.SetDistantApplicationEntityTitle(aet); |
167 c.SetDistantHost(address); | |
168 c.SetDistantPort(port); | |
169 c.Open(); | |
170 } | |
171 | |
62 | 172 bool OrthancRestApi::MergeQueryAndTemplate(DicomMap& result, |
50 | 173 const std::string& postData) |
0 | 174 { |
175 Json::Value query; | |
176 Json::Reader reader; | |
177 | |
178 if (!reader.parse(postData, query) || | |
8 | 179 query.type() != Json::objectValue) |
0 | 180 { |
181 return false; | |
182 } | |
183 | |
184 Json::Value::Members members = query.getMemberNames(); | |
185 for (size_t i = 0; i < members.size(); i++) | |
186 { | |
187 DicomTag t = FromDcmtkBridge::FindTag(members[i]); | |
188 result.SetValue(t, query[members[i]].asString()); | |
189 } | |
190 | |
191 return true; | |
192 } | |
193 | |
62 | 194 bool OrthancRestApi::DicomFindPatient(Json::Value& result, |
50 | 195 DicomUserConnection& c, |
196 const std::string& postData) | |
0 | 197 { |
198 DicomMap m; | |
199 DicomMap::SetupFindPatientTemplate(m); | |
200 if (!MergeQueryAndTemplate(m, postData)) | |
201 { | |
202 return false; | |
203 } | |
204 | |
205 DicomFindAnswers answers; | |
206 c.FindPatient(answers, m); | |
207 answers.ToJson(result); | |
208 return true; | |
209 } | |
210 | |
62 | 211 bool OrthancRestApi::DicomFindStudy(Json::Value& result, |
50 | 212 DicomUserConnection& c, |
213 const std::string& postData) | |
0 | 214 { |
215 DicomMap m; | |
216 DicomMap::SetupFindStudyTemplate(m); | |
217 if (!MergeQueryAndTemplate(m, postData)) | |
218 { | |
219 return false; | |
220 } | |
221 | |
80 | 222 if (m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && |
223 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) | |
0 | 224 { |
225 return false; | |
226 } | |
227 | |
228 DicomFindAnswers answers; | |
229 c.FindStudy(answers, m); | |
230 answers.ToJson(result); | |
231 return true; | |
232 } | |
233 | |
62 | 234 bool OrthancRestApi::DicomFindSeries(Json::Value& result, |
50 | 235 DicomUserConnection& c, |
236 const std::string& postData) | |
0 | 237 { |
238 DicomMap m; | |
239 DicomMap::SetupFindSeriesTemplate(m); | |
240 if (!MergeQueryAndTemplate(m, postData)) | |
241 { | |
242 return false; | |
243 } | |
244 | |
80 | 245 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && |
246 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) || | |
247 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2) | |
0 | 248 { |
249 return false; | |
250 } | |
251 | |
252 DicomFindAnswers answers; | |
253 c.FindSeries(answers, m); | |
254 answers.ToJson(result); | |
255 return true; | |
256 } | |
257 | |
62 | 258 bool OrthancRestApi::DicomFind(Json::Value& result, |
50 | 259 DicomUserConnection& c, |
260 const std::string& postData) | |
0 | 261 { |
262 DicomMap m; | |
263 DicomMap::SetupFindPatientTemplate(m); | |
264 if (!MergeQueryAndTemplate(m, postData)) | |
265 { | |
266 return false; | |
267 } | |
268 | |
269 DicomFindAnswers patients; | |
270 c.FindPatient(patients, m); | |
271 | |
272 // Loop over the found patients | |
273 result = Json::arrayValue; | |
274 for (size_t i = 0; i < patients.GetSize(); i++) | |
275 { | |
276 Json::Value patient(Json::objectValue); | |
277 FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i)); | |
278 | |
279 DicomMap::SetupFindStudyTemplate(m); | |
280 if (!MergeQueryAndTemplate(m, postData)) | |
281 { | |
282 return false; | |
283 } | |
80 | 284 m.CopyTagIfExists(patients.GetAnswer(i), DICOM_TAG_PATIENT_ID); |
0 | 285 |
286 DicomFindAnswers studies; | |
287 c.FindStudy(studies, m); | |
288 | |
289 patient["Studies"] = Json::arrayValue; | |
290 | |
291 // Loop over the found studies | |
292 for (size_t j = 0; j < studies.GetSize(); j++) | |
293 { | |
294 Json::Value study(Json::objectValue); | |
295 FromDcmtkBridge::ToJson(study, studies.GetAnswer(j)); | |
296 | |
297 DicomMap::SetupFindSeriesTemplate(m); | |
298 if (!MergeQueryAndTemplate(m, postData)) | |
299 { | |
300 return false; | |
301 } | |
80 | 302 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_PATIENT_ID); |
303 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_STUDY_INSTANCE_UID); | |
0 | 304 |
305 DicomFindAnswers series; | |
306 c.FindSeries(series, m); | |
307 | |
308 // Loop over the found series | |
309 study["Series"] = Json::arrayValue; | |
310 for (size_t k = 0; k < series.GetSize(); k++) | |
311 { | |
312 Json::Value series2(Json::objectValue); | |
313 FromDcmtkBridge::ToJson(series2, series.GetAnswer(k)); | |
314 study["Series"].append(series2); | |
315 } | |
316 | |
317 patient["Studies"].append(study); | |
318 } | |
319 | |
320 result.append(patient); | |
321 } | |
322 | |
323 return true; | |
324 } | |
325 | |
326 | |
327 | |
62 | 328 bool OrthancRestApi::DicomStore(Json::Value& result, |
50 | 329 DicomUserConnection& c, |
330 const std::string& postData) | |
0 | 331 { |
332 Json::Value found(Json::objectValue); | |
333 | |
334 if (!Toolbox::IsUuid(postData)) | |
335 { | |
336 // This is not a UUID, assume this is a DICOM instance | |
337 c.Store(postData); | |
338 } | |
339 else if (index_.GetSeries(found, postData)) | |
340 { | |
341 // The UUID corresponds to a series | |
342 for (size_t i = 0; i < found["Instances"].size(); i++) | |
343 { | |
344 std::string uuid = found["Instances"][i].asString(); | |
345 Json::Value instance(Json::objectValue); | |
346 if (index_.GetInstance(instance, uuid)) | |
347 { | |
348 std::string content; | |
349 storage_.ReadFile(content, instance["FileUuid"].asString()); | |
350 c.Store(content); | |
351 } | |
352 else | |
353 { | |
354 return false; | |
355 } | |
356 } | |
357 } | |
358 else if (index_.GetInstance(found, postData)) | |
359 { | |
360 // The UUID corresponds to an instance | |
361 std::string content; | |
362 storage_.ReadFile(content, found["FileUuid"].asString()); | |
363 c.Store(content); | |
364 } | |
365 else | |
366 { | |
367 return false; | |
368 } | |
369 | |
370 return true; | |
371 } | |
372 | |
373 | |
62 | 374 OrthancRestApi::OrthancRestApi(ServerIndex& index, |
50 | 375 const std::string& path) : |
0 | 376 index_(index), |
377 storage_(path) | |
378 { | |
379 GetListOfDicomModalities(modalities_); | |
380 } | |
381 | |
382 | |
62 | 383 void OrthancRestApi::Handle( |
0 | 384 HttpOutput& output, |
385 const std::string& method, | |
386 const UriComponents& uri, | |
387 const Arguments& headers, | |
388 const Arguments& arguments, | |
389 const std::string& postData) | |
390 { | |
391 if (uri.size() == 0) | |
392 { | |
393 if (method == "GET") | |
394 { | |
85
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
395 output.Redirect("app/explorer.html"); |
0 | 396 } |
397 else | |
398 { | |
399 output.SendMethodNotAllowedError("GET"); | |
400 } | |
401 | |
402 return; | |
403 } | |
404 | |
405 bool existingResource = false; | |
406 Json::Value result(Json::objectValue); | |
407 | |
408 | |
409 // List all the instances --------------------------------------------------- | |
410 | |
411 if (uri.size() == 1 && uri[0] == "instances") | |
412 { | |
413 if (method == "GET") | |
414 { | |
415 result = Json::Value(Json::arrayValue); | |
416 index_.GetAllUuids(result, "Instances"); | |
417 existingResource = true; | |
418 } | |
419 else if (method == "POST") | |
420 { | |
421 // Add a new instance to the storage | |
422 if (Store(result, postData)) | |
423 { | |
424 SendJson(output, result); | |
425 return; | |
426 } | |
427 else | |
428 { | |
62 | 429 output.SendHeader(Orthanc_HttpStatus_415_UnsupportedMediaType); |
0 | 430 return; |
431 } | |
432 } | |
433 else | |
434 { | |
435 output.SendMethodNotAllowedError("GET,POST"); | |
436 return; | |
437 } | |
438 } | |
439 | |
440 | |
441 // List all the patients, studies or series --------------------------------- | |
442 | |
443 if (uri.size() == 1 && | |
444 (uri[0] == "series" || | |
445 uri[0] == "studies" || | |
446 uri[0] == "patients")) | |
447 { | |
448 if (method == "GET") | |
449 { | |
450 result = Json::Value(Json::arrayValue); | |
451 | |
452 if (uri[0] == "instances") | |
453 index_.GetAllUuids(result, "Instances"); | |
454 else if (uri[0] == "series") | |
455 index_.GetAllUuids(result, "Series"); | |
456 else if (uri[0] == "studies") | |
457 index_.GetAllUuids(result, "Studies"); | |
458 else if (uri[0] == "patients") | |
459 index_.GetAllUuids(result, "Patients"); | |
460 | |
461 existingResource = true; | |
462 } | |
463 else | |
464 { | |
465 output.SendMethodNotAllowedError("GET"); | |
466 return; | |
467 } | |
468 } | |
469 | |
470 | |
471 // Information about a single object ---------------------------------------- | |
472 | |
473 else if (uri.size() == 2 && | |
474 (uri[0] == "instances" || | |
475 uri[0] == "series" || | |
476 uri[0] == "studies" || | |
477 uri[0] == "patients")) | |
478 { | |
479 if (method == "GET") | |
480 { | |
481 if (uri[0] == "patients") | |
482 { | |
483 existingResource = index_.GetPatient(result, uri[1]); | |
484 } | |
485 else if (uri[0] == "studies") | |
486 { | |
487 existingResource = index_.GetStudy(result, uri[1]); | |
488 } | |
489 else if (uri[0] == "series") | |
490 { | |
491 existingResource = index_.GetSeries(result, uri[1]); | |
492 } | |
493 else if (uri[0] == "instances") | |
494 { | |
495 existingResource = index_.GetInstance(result, uri[1]); | |
496 } | |
497 } | |
498 else if (method == "DELETE") | |
499 { | |
500 if (uri[0] == "patients") | |
501 { | |
502 existingResource = index_.DeletePatient(result, uri[1]); | |
503 } | |
504 else if (uri[0] == "studies") | |
505 { | |
506 existingResource = index_.DeleteStudy(result, uri[1]); | |
507 } | |
508 else if (uri[0] == "series") | |
509 { | |
510 existingResource = index_.DeleteSeries(result, uri[1]); | |
511 } | |
512 else if (uri[0] == "instances") | |
513 { | |
514 existingResource = index_.DeleteInstance(result, uri[1]); | |
515 } | |
516 | |
517 if (existingResource) | |
518 { | |
519 result["Status"] = "Success"; | |
520 } | |
521 } | |
522 else | |
523 { | |
524 output.SendMethodNotAllowedError("GET,DELETE"); | |
525 return; | |
526 } | |
527 } | |
528 | |
529 | |
530 // Get the DICOM or the JSON file of one instance --------------------------- | |
531 | |
532 else if (uri.size() == 3 && | |
533 uri[0] == "instances" && | |
534 (uri[2] == "file" || | |
34
96e57b863dd9
option to disallow remote access
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
8
diff
changeset
|
535 uri[2] == "tags" || |
35
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
536 uri[2] == "simplified-tags")) |
0 | 537 { |
538 std::string fileUuid, contentType; | |
539 if (uri[2] == "file") | |
540 { | |
541 existingResource = index_.GetDicomFile(fileUuid, uri[1]); | |
542 contentType = "application/dicom"; | |
543 } | |
34
96e57b863dd9
option to disallow remote access
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
8
diff
changeset
|
544 else if (uri[2] == "tags" || |
35
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
545 uri[2] == "simplified-tags") |
0 | 546 { |
547 existingResource = index_.GetJsonFile(fileUuid, uri[1]); | |
548 contentType = "application/json"; | |
549 } | |
550 | |
551 if (existingResource) | |
552 { | |
35
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
553 if (uri[2] == "simplified-tags") |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
554 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
555 Json::Value v; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
556 SimplifyTags(v, storage_, fileUuid); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
557 SendJson(output, v); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
558 return; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
559 } |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
560 else |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
561 { |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
562 output.AnswerFile(storage_, fileUuid, contentType); |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
563 return; |
f6d12037f886
full json vs. simplified json
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
34
diff
changeset
|
564 } |
0 | 565 } |
566 } | |
567 | |
568 | |
569 else if (uri.size() == 3 && | |
570 uri[0] == "instances" && | |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
571 uri[2] == "frames") |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
572 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
573 Json::Value instance(Json::objectValue); |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
574 existingResource = index_.GetInstance(instance, uri[1]); |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
575 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
576 if (existingResource) |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
577 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
578 result = Json::arrayValue; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
579 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
580 unsigned int numberOfFrames = 1; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
581 try |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
582 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
583 Json::Value tmp = instance["MainDicomTags"]["NumberOfFrames"]; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
584 numberOfFrames = boost::lexical_cast<unsigned int>(tmp.asString()); |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
585 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
586 catch (boost::bad_lexical_cast) |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
587 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
588 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
589 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
590 for (unsigned int i = 0; i < numberOfFrames; i++) |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
591 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
592 result.append(i); |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
593 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
594 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
595 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
596 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
597 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
598 else if (uri[0] == "instances" && |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
599 ((uri.size() == 3 && |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
600 (uri[2] == "preview" || |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
601 uri[2] == "image-uint8" || |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
602 uri[2] == "image-uint16")) || |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
603 (uri.size() == 5 && |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
604 uri[2] == "frames" && |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
605 (uri[4] == "preview" || |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
606 uri[4] == "image-uint8" || |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
607 uri[4] == "image-uint16")))) |
0 | 608 { |
609 std::string uuid; | |
610 existingResource = index_.GetDicomFile(uuid, uri[1]); | |
611 | |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
612 std::string action = uri[2]; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
613 |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
614 unsigned int frame = 0; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
615 if (existingResource && |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
616 uri.size() == 5) |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
617 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
618 // Access to multi-frame image |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
619 action = uri[4]; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
620 try |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
621 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
622 frame = boost::lexical_cast<unsigned int>(uri[3]); |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
623 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
624 catch (boost::bad_lexical_cast) |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
625 { |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
626 existingResource = false; |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
627 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
628 } |
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
629 |
0 | 630 if (existingResource) |
631 { | |
632 std::string dicomContent, png; | |
633 storage_.ReadFile(dicomContent, uuid); | |
634 try | |
635 { | |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
636 if (action == "preview") |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
637 { |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
638 FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_Preview); |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
639 } |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
640 else if (action == "image-uint8") |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
641 { |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
642 FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt8); |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
643 } |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
644 else if (action == "image-uint16") |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
645 { |
53
293038baf8f1
access to multi-frame images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
50
diff
changeset
|
646 FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt16); |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
647 } |
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
648 else |
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
649 { |
62 | 650 throw OrthancException(ErrorCode_InternalError); |
42
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
651 } |
ea48f38afe5f
access to raw images
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
652 |
0 | 653 output.AnswerBufferWithContentType(png, "image/png"); |
654 return; | |
655 } | |
62 | 656 catch (OrthancException&) |
0 | 657 { |
85
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
658 std::string root = ""; |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
659 for (size_t i = 1; i < uri.size(); i++) |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
660 { |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
661 root += "../"; |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
662 } |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
663 |
ebce15865cce
relative redirection
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
80
diff
changeset
|
664 output.Redirect(root + "app/images/Unsupported.png"); |
0 | 665 return; |
666 } | |
667 } | |
668 } | |
669 | |
670 | |
671 | |
672 // Changes API -------------------------------------------------------------- | |
673 | |
674 if (uri.size() == 1 && uri[0] == "changes") | |
675 { | |
676 if (method == "GET") | |
677 { | |
678 const static unsigned int MAX_RESULTS = 100; | |
679 | |
680 std::string filter = GetArgument(arguments, "filter", ""); | |
681 int64_t since; | |
682 unsigned int limit; | |
683 try | |
684 { | |
685 since = boost::lexical_cast<int64_t>(GetArgument(arguments, "since", "0")); | |
686 limit = boost::lexical_cast<unsigned int>(GetArgument(arguments, "limit", "0")); | |
687 } | |
688 catch (boost::bad_lexical_cast) | |
689 { | |
62 | 690 output.SendHeader(Orthanc_HttpStatus_400_BadRequest); |
0 | 691 return; |
692 } | |
693 | |
694 if (limit == 0 || limit > MAX_RESULTS) | |
695 { | |
696 limit = MAX_RESULTS; | |
697 } | |
698 | |
699 if (!index_.GetChanges(result, since, filter, limit)) | |
700 { | |
62 | 701 output.SendHeader(Orthanc_HttpStatus_400_BadRequest); |
0 | 702 return; |
703 } | |
704 | |
705 existingResource = true; | |
706 } | |
707 else | |
708 { | |
709 output.SendMethodNotAllowedError("GET"); | |
710 return; | |
711 } | |
712 } | |
713 | |
714 | |
715 // DICOM bridge ------------------------------------------------------------- | |
716 | |
717 if (uri.size() == 1 && | |
718 uri[0] == "modalities") | |
719 { | |
720 if (method == "GET") | |
721 { | |
722 result = Json::Value(Json::arrayValue); | |
723 existingResource = true; | |
724 | |
725 for (Modalities::const_iterator it = modalities_.begin(); | |
726 it != modalities_.end(); it++) | |
727 { | |
728 result.append(*it); | |
729 } | |
730 } | |
731 else | |
732 { | |
733 output.SendMethodNotAllowedError("GET"); | |
734 return; | |
735 } | |
736 } | |
737 | |
738 if ((uri.size() == 2 || | |
739 uri.size() == 3) && | |
740 uri[0] == "modalities") | |
741 { | |
742 if (modalities_.find(uri[1]) == modalities_.end()) | |
743 { | |
744 // Unknown modality | |
745 } | |
746 else if (uri.size() == 2) | |
747 { | |
748 if (method != "GET") | |
749 { | |
750 output.SendMethodNotAllowedError("POST"); | |
751 return; | |
752 } | |
753 else | |
754 { | |
755 existingResource = true; | |
756 result = Json::arrayValue; | |
757 result.append("find-patient"); | |
758 result.append("find-study"); | |
759 result.append("find-series"); | |
760 result.append("find"); | |
761 result.append("store"); | |
762 } | |
763 } | |
764 else if (uri.size() == 3) | |
765 { | |
766 if (uri[2] != "find-patient" && | |
767 uri[2] != "find-study" && | |
768 uri[2] != "find-series" && | |
769 uri[2] != "find" && | |
770 uri[2] != "store") | |
771 { | |
772 // Unknown request | |
773 } | |
774 else if (method != "POST") | |
775 { | |
776 output.SendMethodNotAllowedError("POST"); | |
777 return; | |
778 } | |
779 else | |
780 { | |
781 DicomUserConnection connection; | |
782 ConnectToModality(connection, uri[1]); | |
783 existingResource = true; | |
784 | |
785 if ((uri[2] == "find-patient" && !DicomFindPatient(result, connection, postData)) || | |
786 (uri[2] == "find-study" && !DicomFindStudy(result, connection, postData)) || | |
787 (uri[2] == "find-series" && !DicomFindSeries(result, connection, postData)) || | |
788 (uri[2] == "find" && !DicomFind(result, connection, postData)) || | |
789 (uri[2] == "store" && !DicomStore(result, connection, postData))) | |
790 { | |
62 | 791 output.SendHeader(Orthanc_HttpStatus_400_BadRequest); |
0 | 792 return; |
793 } | |
794 } | |
795 } | |
796 } | |
797 | |
798 | |
799 if (existingResource) | |
800 { | |
801 SendJson(output, result); | |
802 } | |
803 else | |
804 { | |
62 | 805 output.SendHeader(Orthanc_HttpStatus_404_NotFound); |
0 | 806 } |
807 } | |
808 } |