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

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 May 2024 13:37:23 +0200
parents a3732285f8b6
children 4690a0d2b01e
comparison
equal deleted inserted replaced
5607:a3732285f8b6 5608:3d0aa94b44b3
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **/ 20 **/
21 21
22 22
23 #include "../PrecompiledHeadersServer.h" 23 #include "../PrecompiledHeadersServer.h"
24 #include "../ResourceFinder.h"
25
24 #include "OrthancRestApi.h" 26 #include "OrthancRestApi.h"
25 27
26 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h" 28 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h"
27 #include "../../../OrthancFramework/Sources/DicomFormat/DicomImageInformation.h" 29 #include "../../../OrthancFramework/Sources/DicomFormat/DicomImageInformation.h"
28 #include "../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h" 30 #include "../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h"
194 std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags; 196 std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags;
195 std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson; 197 std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson;
196 198
197 AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); 199 AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess);
198 } 200 }
199
200
201 class ResourceFinder : public boost::noncopyable
202 {
203 private:
204 FindRequest request_;
205 bool expand_;
206 std::set<DicomTag> requestedTags_;
207 DicomToJsonFormat format_;
208 bool includeAllMetadata_; // Same as: ExpandResourceFlags_IncludeAllMetadata
209
210 SeriesStatus GetSeriesStatus(uint32_t& expectedNumberOfInstances,
211 const FindResponse::Resource& resource) const
212 {
213 if (request_.GetLevel() != ResourceType_Series)
214 {
215 throw OrthancException(ErrorCode_BadParameterType);
216 }
217
218 std::string s;
219 if (!resource.LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
220 !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
221 {
222 return SeriesStatus_Unknown;
223 }
224
225 std::list<std::string> values;
226 if (!resource.LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
227 {
228 throw OrthancException(ErrorCode_BadSequenceOfCalls);
229 }
230
231 std::set<int64_t> instances;
232
233 for (std::list<std::string>::const_iterator
234 it = values.begin(); it != values.end(); ++it)
235 {
236 int64_t index;
237
238 if (!SerializationToolbox::ParseInteger64(index, *it))
239 {
240 return SeriesStatus_Unknown;
241 }
242
243 if (index <= 0 ||
244 index > static_cast<int64_t>(expectedNumberOfInstances))
245 {
246 // Out-of-range instance index
247 return SeriesStatus_Inconsistent;
248 }
249
250 if (instances.find(index) != instances.end())
251 {
252 // Twice the same instance index
253 return SeriesStatus_Inconsistent;
254 }
255
256 instances.insert(index);
257 }
258
259 if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
260 {
261 return SeriesStatus_Complete;
262 }
263 else
264 {
265 return SeriesStatus_Missing;
266 }
267 }
268
269
270 void Expand(Json::Value& target,
271 const FindResponse::Resource& resource,
272 ServerIndex& index) const
273 {
274 /**
275 * This method closely follows "SerializeExpandedResource()" in
276 * "ServerContext.cpp" from Orthanc 1.12.3.
277 **/
278
279 if (resource.GetLevel() != request_.GetLevel())
280 {
281 throw OrthancException(ErrorCode_InternalError);
282 }
283
284 if (!requestedTags_.empty())
285 {
286 throw OrthancException(ErrorCode_NotImplemented);
287 }
288
289 target = Json::objectValue;
290
291 target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
292 target["ID"] = resource.GetIdentifier();
293
294 switch (resource.GetLevel())
295 {
296 case ResourceType_Patient:
297 break;
298
299 case ResourceType_Study:
300 target["ParentPatient"] = resource.GetParentIdentifier();
301 break;
302
303 case ResourceType_Series:
304 target["ParentStudy"] = resource.GetParentIdentifier();
305 break;
306
307 case ResourceType_Instance:
308 target["ParentSeries"] = resource.GetParentIdentifier();
309 break;
310
311 default:
312 throw OrthancException(ErrorCode_InternalError);
313 }
314
315 if (resource.GetLevel() != ResourceType_Instance)
316 {
317 const std::set<std::string>& children = resource.GetChildrenIdentifiers();
318
319 Json::Value c = Json::arrayValue;
320 for (std::set<std::string>::const_iterator
321 it = children.begin(); it != children.end(); ++it)
322 {
323 c.append(*it);
324 }
325
326 switch (resource.GetLevel())
327 {
328 case ResourceType_Patient:
329 target["Studies"] = c;
330 break;
331
332 case ResourceType_Study:
333 target["Series"] = c;
334 break;
335
336 case ResourceType_Series:
337 target["Instances"] = c;
338 break;
339
340 default:
341 throw OrthancException(ErrorCode_InternalError);
342 }
343 }
344
345 switch (resource.GetLevel())
346 {
347 case ResourceType_Patient:
348 case ResourceType_Study:
349 break;
350
351 case ResourceType_Series:
352 {
353 uint32_t expectedNumberOfInstances;
354 SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
355
356 target["Status"] = EnumerationToString(status);
357
358 static const char* EXPECTED_NUMBER_OF_INSTANCES = "ExpectedNumberOfInstances";
359
360 if (status == SeriesStatus_Unknown)
361 {
362 target[EXPECTED_NUMBER_OF_INSTANCES] = Json::nullValue;
363 }
364 else
365 {
366 target[EXPECTED_NUMBER_OF_INSTANCES] = expectedNumberOfInstances;
367 }
368
369 break;
370 }
371
372 case ResourceType_Instance:
373 {
374 FileInfo info;
375 if (resource.LookupAttachment(info, FileContentType_Dicom))
376 {
377 target["FileSize"] = static_cast<Json::UInt64>(info.GetUncompressedSize());
378 target["FileUuid"] = info.GetUuid();
379 }
380 else
381 {
382 throw OrthancException(ErrorCode_InternalError);
383 }
384
385 static const char* INDEX_IN_SERIES = "IndexInSeries";
386
387 std::string s;
388 uint32_t index;
389 if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
390 SerializationToolbox::ParseUnsignedInteger32(index, s))
391 {
392 target[INDEX_IN_SERIES] = index;
393 }
394 else
395 {
396 target[INDEX_IN_SERIES] = Json::nullValue;
397 }
398
399 break;
400 }
401
402 default:
403 throw OrthancException(ErrorCode_InternalError);
404 }
405
406 std::string s;
407 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
408 {
409 target["AnonymizedFrom"] = s;
410 }
411
412 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
413 {
414 target["ModifiedFrom"] = s;
415 }
416
417 if (resource.GetLevel() == ResourceType_Patient ||
418 resource.GetLevel() == ResourceType_Study ||
419 resource.GetLevel() == ResourceType_Series)
420 {
421 target["IsStable"] = !index.IsUnstableResource(resource.GetLevel(), resource.GetInternalId());
422
423 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
424 {
425 target["LastUpdate"] = s;
426 }
427 }
428
429 {
430 static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
431 static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags";
432
433 // TODO-FIND : (expandFlags & ExpandResourceFlags_IncludeMainDicomTags)
434 DicomMap allMainDicomTags;
435 resource.GetMainDicomTags(allMainDicomTags, resource.GetLevel());
436
437 DicomMap levelMainDicomTags;
438 allMainDicomTags.ExtractResourceInformation(levelMainDicomTags, resource.GetLevel());
439
440 target[MAIN_DICOM_TAGS] = Json::objectValue;
441 FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], levelMainDicomTags, format_);
442
443 if (resource.GetLevel() == ResourceType_Study)
444 {
445 DicomMap patientMainDicomTags;
446 allMainDicomTags.ExtractPatientInformation(patientMainDicomTags);
447
448 target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue;
449 FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format_);
450 }
451
452 /*
453 TODO-FIND
454
455 if (!requestedTags_.empty())
456 {
457 static const char* const REQUESTED_TAGS = "RequestedTags";
458
459 DicomMap tags;
460 resource.GetMainDicomTags().ExtractTags(tags, requestedTags);
461
462 target[REQUESTED_TAGS] = Json::objectValue;
463 FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format);
464 }
465 */
466 }
467
468 {
469 Json::Value labels = Json::arrayValue;
470
471 for (std::set<std::string>::const_iterator
472 it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
473 {
474 labels.append(*it);
475 }
476
477 target["Labels"] = labels;
478 }
479
480 if (includeAllMetadata_) // new in Orthanc 1.12.4
481 {
482 const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
483
484 Json::Value metadata = Json::objectValue;
485
486 for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
487 {
488 metadata[EnumerationToString(it->first)] = it->second;
489 }
490
491 target["Metadata"] = metadata;
492 }
493 }
494
495
496 public:
497 ResourceFinder(ResourceType level,
498 bool expand) :
499 request_(level),
500 expand_(expand),
501 format_(DicomToJsonFormat_Human),
502 includeAllMetadata_(false)
503 {
504 if (expand)
505 {
506 request_.SetRetrieveMainDicomTags(level, true);
507 request_.SetRetrieveMetadata(level, true);
508 request_.SetRetrieveLabels(true);
509
510 if (level == ResourceType_Series)
511 {
512 request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
513 }
514
515 if (level == ResourceType_Instance)
516 {
517 request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
518 }
519 else
520 {
521 request_.SetRetrieveChildrenIdentifiers(true);
522 }
523
524 if (level != ResourceType_Patient)
525 {
526 request_.SetRetrieveParentIdentifier(true);
527 }
528 }
529 }
530
531 void SetRequestedTags(const std::set<DicomTag>& tags)
532 {
533 requestedTags_ = tags;
534 }
535
536 void SetFormat(DicomToJsonFormat format)
537 {
538 format_ = format;
539 }
540
541 void SetLimits(uint64_t since,
542 uint64_t count)
543 {
544 request_.SetLimits(since, count);
545 }
546
547 void SetIncludeAllMetadata(bool include)
548 {
549 includeAllMetadata_ = include;
550 }
551
552 void Execute(Json::Value& target,
553 ServerContext& context)
554 {
555 FindResponse response;
556 context.GetIndex().ExecuteFind(response, request_);
557
558 target = Json::arrayValue;
559
560 if (expand_)
561 {
562 for (size_t i = 0; i < response.GetSize(); i++)
563 {
564 Json::Value item;
565 Expand(item, response.GetResource(i), context.GetIndex());
566 target.append(item);
567 }
568 }
569 else
570 {
571 for (size_t i = 0; i < response.GetSize(); i++)
572 {
573 target.append(response.GetResource(i).GetIdentifier());
574 }
575 }
576 }
577 };
578 201
579 202
580 template <enum ResourceType resourceType> 203 template <enum ResourceType resourceType>
581 static void ListResources(RestApiGetCall& call) 204 static void ListResources(RestApiGetCall& call)
582 { 205 {