comparison OrthancServer/Sources/ResourceFinder.cpp @ 5608:3d0aa94b44b3 find-refactoring

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 May 2024 13:37:23 +0200
parents
children 4690a0d2b01e
comparison
equal deleted inserted replaced
5607:a3732285f8b6 5608:3d0aa94b44b3
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2024 Osimis S.A., Belgium
6 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
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/>.
20 **/
21
22
23 #include "PrecompiledHeadersServer.h"
24 #include "ResourceFinder.h"
25
26 #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
27 #include "../../OrthancFramework/Sources/OrthancException.h"
28 #include "../../OrthancFramework/Sources/SerializationToolbox.h"
29 #include "ServerContext.h"
30 #include "ServerIndex.h"
31
32
33 namespace Orthanc
34 {
35 SeriesStatus ResourceFinder::GetSeriesStatus(uint32_t& expectedNumberOfInstances,
36 const FindResponse::Resource& resource) const
37 {
38 if (request_.GetLevel() != ResourceType_Series)
39 {
40 throw OrthancException(ErrorCode_BadParameterType);
41 }
42
43 std::string s;
44 if (!resource.LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
45 !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
46 {
47 return SeriesStatus_Unknown;
48 }
49
50 std::list<std::string> values;
51 if (!resource.LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
52 {
53 throw OrthancException(ErrorCode_BadSequenceOfCalls);
54 }
55
56 std::set<int64_t> instances;
57
58 for (std::list<std::string>::const_iterator
59 it = values.begin(); it != values.end(); ++it)
60 {
61 int64_t index;
62
63 if (!SerializationToolbox::ParseInteger64(index, *it))
64 {
65 return SeriesStatus_Unknown;
66 }
67
68 if (index <= 0 ||
69 index > static_cast<int64_t>(expectedNumberOfInstances))
70 {
71 // Out-of-range instance index
72 return SeriesStatus_Inconsistent;
73 }
74
75 if (instances.find(index) != instances.end())
76 {
77 // Twice the same instance index
78 return SeriesStatus_Inconsistent;
79 }
80
81 instances.insert(index);
82 }
83
84 if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
85 {
86 return SeriesStatus_Complete;
87 }
88 else
89 {
90 return SeriesStatus_Missing;
91 }
92 }
93
94
95 void ResourceFinder::Expand(Json::Value& target,
96 const FindResponse::Resource& resource,
97 ServerIndex& index) const
98 {
99 /**
100 * This method closely follows "SerializeExpandedResource()" in
101 * "ServerContext.cpp" from Orthanc 1.12.3.
102 **/
103
104 if (resource.GetLevel() != request_.GetLevel())
105 {
106 throw OrthancException(ErrorCode_InternalError);
107 }
108
109 if (!requestedTags_.empty())
110 {
111 throw OrthancException(ErrorCode_NotImplemented);
112 }
113
114 target = Json::objectValue;
115
116 target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
117 target["ID"] = resource.GetIdentifier();
118
119 switch (resource.GetLevel())
120 {
121 case ResourceType_Patient:
122 break;
123
124 case ResourceType_Study:
125 target["ParentPatient"] = resource.GetParentIdentifier();
126 break;
127
128 case ResourceType_Series:
129 target["ParentStudy"] = resource.GetParentIdentifier();
130 break;
131
132 case ResourceType_Instance:
133 target["ParentSeries"] = resource.GetParentIdentifier();
134 break;
135
136 default:
137 throw OrthancException(ErrorCode_InternalError);
138 }
139
140 if (resource.GetLevel() != ResourceType_Instance)
141 {
142 const std::set<std::string>& children = resource.GetChildrenIdentifiers();
143
144 Json::Value c = Json::arrayValue;
145 for (std::set<std::string>::const_iterator
146 it = children.begin(); it != children.end(); ++it)
147 {
148 c.append(*it);
149 }
150
151 switch (resource.GetLevel())
152 {
153 case ResourceType_Patient:
154 target["Studies"] = c;
155 break;
156
157 case ResourceType_Study:
158 target["Series"] = c;
159 break;
160
161 case ResourceType_Series:
162 target["Instances"] = c;
163 break;
164
165 default:
166 throw OrthancException(ErrorCode_InternalError);
167 }
168 }
169
170 switch (resource.GetLevel())
171 {
172 case ResourceType_Patient:
173 case ResourceType_Study:
174 break;
175
176 case ResourceType_Series:
177 {
178 uint32_t expectedNumberOfInstances;
179 SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
180
181 target["Status"] = EnumerationToString(status);
182
183 static const char* EXPECTED_NUMBER_OF_INSTANCES = "ExpectedNumberOfInstances";
184
185 if (status == SeriesStatus_Unknown)
186 {
187 target[EXPECTED_NUMBER_OF_INSTANCES] = Json::nullValue;
188 }
189 else
190 {
191 target[EXPECTED_NUMBER_OF_INSTANCES] = expectedNumberOfInstances;
192 }
193
194 break;
195 }
196
197 case ResourceType_Instance:
198 {
199 FileInfo info;
200 if (resource.LookupAttachment(info, FileContentType_Dicom))
201 {
202 target["FileSize"] = static_cast<Json::UInt64>(info.GetUncompressedSize());
203 target["FileUuid"] = info.GetUuid();
204 }
205 else
206 {
207 throw OrthancException(ErrorCode_InternalError);
208 }
209
210 static const char* INDEX_IN_SERIES = "IndexInSeries";
211
212 std::string s;
213 uint32_t index;
214 if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
215 SerializationToolbox::ParseUnsignedInteger32(index, s))
216 {
217 target[INDEX_IN_SERIES] = index;
218 }
219 else
220 {
221 target[INDEX_IN_SERIES] = Json::nullValue;
222 }
223
224 break;
225 }
226
227 default:
228 throw OrthancException(ErrorCode_InternalError);
229 }
230
231 std::string s;
232 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
233 {
234 target["AnonymizedFrom"] = s;
235 }
236
237 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
238 {
239 target["ModifiedFrom"] = s;
240 }
241
242 if (resource.GetLevel() == ResourceType_Patient ||
243 resource.GetLevel() == ResourceType_Study ||
244 resource.GetLevel() == ResourceType_Series)
245 {
246 target["IsStable"] = !index.IsUnstableResource(resource.GetLevel(), resource.GetInternalId());
247
248 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
249 {
250 target["LastUpdate"] = s;
251 }
252 }
253
254 {
255 static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
256 static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags";
257
258 // TODO-FIND : (expandFlags & ExpandResourceFlags_IncludeMainDicomTags)
259 DicomMap allMainDicomTags;
260 resource.GetMainDicomTags(allMainDicomTags, resource.GetLevel());
261
262 DicomMap levelMainDicomTags;
263 allMainDicomTags.ExtractResourceInformation(levelMainDicomTags, resource.GetLevel());
264
265 target[MAIN_DICOM_TAGS] = Json::objectValue;
266 FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], levelMainDicomTags, format_);
267
268 if (resource.GetLevel() == ResourceType_Study)
269 {
270 DicomMap patientMainDicomTags;
271 allMainDicomTags.ExtractPatientInformation(patientMainDicomTags);
272
273 target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue;
274 FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format_);
275 }
276
277 /*
278 TODO-FIND
279
280 if (!requestedTags_.empty())
281 {
282 static const char* const REQUESTED_TAGS = "RequestedTags";
283
284 DicomMap tags;
285 resource.GetMainDicomTags().ExtractTags(tags, requestedTags);
286
287 target[REQUESTED_TAGS] = Json::objectValue;
288 FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format);
289 }
290 */
291 }
292
293 {
294 Json::Value labels = Json::arrayValue;
295
296 for (std::set<std::string>::const_iterator
297 it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
298 {
299 labels.append(*it);
300 }
301
302 target["Labels"] = labels;
303 }
304
305 if (includeAllMetadata_) // new in Orthanc 1.12.4
306 {
307 const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
308
309 Json::Value metadata = Json::objectValue;
310
311 for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
312 {
313 metadata[EnumerationToString(it->first)] = it->second;
314 }
315
316 target["Metadata"] = metadata;
317 }
318 }
319
320
321 ResourceFinder::ResourceFinder(ResourceType level,
322 bool expand) :
323 request_(level),
324 expand_(expand),
325 format_(DicomToJsonFormat_Human),
326 includeAllMetadata_(false)
327 {
328 if (expand)
329 {
330 request_.SetRetrieveMainDicomTags(level, true);
331 request_.SetRetrieveMetadata(level, true);
332 request_.SetRetrieveLabels(true);
333
334 if (level == ResourceType_Series)
335 {
336 request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
337 }
338
339 if (level == ResourceType_Instance)
340 {
341 request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
342 }
343 else
344 {
345 request_.SetRetrieveChildrenIdentifiers(true);
346 }
347
348 if (level != ResourceType_Patient)
349 {
350 request_.SetRetrieveParentIdentifier(true);
351 }
352 }
353 }
354
355
356 void ResourceFinder::Execute(Json::Value& target,
357 ServerContext& context)
358 {
359 FindResponse response;
360 context.GetIndex().ExecuteFind(response, request_);
361
362 target = Json::arrayValue;
363
364 if (expand_)
365 {
366 for (size_t i = 0; i < response.GetSize(); i++)
367 {
368 Json::Value item;
369 Expand(item, response.GetResource(i), context.GetIndex());
370 target.append(item);
371 }
372 }
373 else
374 {
375 for (size_t i = 0; i < response.GetSize(); i++)
376 {
377 target.append(response.GetResource(i).GetIdentifier());
378 }
379 }
380 }
381 }