comparison OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp @ 5600:8796c100aaf8 find-refactoring

introduction of ResourceFinder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 May 2024 21:23:34 +0200
parents 81a29ad7fb4b
children e4e7ca3d206e
comparison
equal deleted inserted replaced
5596:81a29ad7fb4b 5600:8796c100aaf8
196 196
197 AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); 197 AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess);
198 } 198 }
199 199
200 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) const
272 {
273 /**
274
275 TODO-FIND:
276
277 - Metadata / Series / ExpectedNumberOfInstances
278
279 - Metadata / Series / Status
280
281 - Metadata / Instance / FileSize
282
283 - Metadata / Instance / FileUuid
284
285 - Metadata / Instance / IndexInSeries
286
287 - Metadata / AnonymizedFrom
288
289 - Metadata / ModifiedFrom
290
291 **/
292
293 /**
294 * This method closely follows "SerializeExpandedResource()" in
295 * "ServerContext.cpp" from Orthanc 1.12.3.
296 **/
297
298 if (resource.GetLevel() != request_.GetLevel())
299 {
300 throw OrthancException(ErrorCode_InternalError);
301 }
302
303 target = Json::objectValue;
304
305 target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
306 target["ID"] = resource.GetIdentifier();
307
308 switch (resource.GetLevel())
309 {
310 case ResourceType_Patient:
311 break;
312
313 case ResourceType_Study:
314 target["ParentPatient"] = resource.GetParentIdentifier();
315 break;
316
317 case ResourceType_Series:
318 target["ParentStudy"] = resource.GetParentIdentifier();
319 break;
320
321 case ResourceType_Instance:
322 target["ParentSeries"] = resource.GetParentIdentifier();
323 break;
324
325 default:
326 throw OrthancException(ErrorCode_InternalError);
327 }
328
329 if (resource.GetLevel() != ResourceType_Instance)
330 {
331 const std::set<std::string>& children = resource.GetChildrenIdentifiers();
332
333 Json::Value c = Json::arrayValue;
334 for (std::set<std::string>::const_iterator
335 it = children.begin(); it != children.end(); ++it)
336 {
337 c.append(*it);
338 }
339
340 switch (resource.GetLevel())
341 {
342 case ResourceType_Patient:
343 target["Studies"] = c;
344 break;
345
346 case ResourceType_Study:
347 target["Series"] = c;
348 break;
349
350 case ResourceType_Series:
351 target["Instances"] = c;
352 break;
353
354 default:
355 throw OrthancException(ErrorCode_InternalError);
356 }
357 }
358
359 switch (resource.GetLevel())
360 {
361 case ResourceType_Patient:
362 case ResourceType_Study:
363 break;
364
365 case ResourceType_Series:
366 {
367 uint32_t expectedNumberOfInstances;
368 SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
369
370 target["Status"] = EnumerationToString(status);
371
372 if (status == SeriesStatus_Unknown)
373 {
374 target["ExpectedNumberOfInstances"] = Json::nullValue;
375 }
376 else
377 {
378 target["ExpectedNumberOfInstances"] = expectedNumberOfInstances;
379 }
380
381 break;
382 }
383
384 case ResourceType_Instance:
385 {
386 FileInfo info;
387 if (resource.LookupAttachment(info, FileContentType_Dicom))
388 {
389 target["FileSize"] = info.GetUncompressedSize();
390 target["FileUuid"] = info.GetUuid();
391 }
392 else
393 {
394 throw OrthancException(ErrorCode_InternalError);
395 }
396
397 std::string s;
398 uint32_t index;
399 if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
400 SerializationToolbox::ParseUnsignedInteger32(index, s))
401 {
402 target["IndexInSeries"] = index;
403 }
404 else
405 {
406 target["IndexInSeries"] = Json::nullValue;
407 }
408
409 break;
410 }
411
412 default:
413 throw OrthancException(ErrorCode_InternalError);
414 }
415
416 std::string s;
417 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
418 {
419 target["AnonymizedFrom"] = s;
420 }
421
422 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
423 {
424 target["ModifiedFrom"] = s;
425 }
426
427 if (resource.GetLevel() == ResourceType_Patient ||
428 resource.GetLevel() == ResourceType_Study ||
429 resource.GetLevel() == ResourceType_Series)
430 {
431 // TODO-FIND: Stable
432
433 /*
434 if (resource.IsStable())
435 {
436 target["IsStable"] = true;
437 }
438 */
439
440 if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
441 {
442 target["LastUpdate"] = s;
443 }
444 }
445
446 {
447 Json::Value labels = Json::arrayValue;
448
449 for (std::set<std::string>::const_iterator
450 it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
451 {
452 labels.append(*it);
453 }
454
455 target["Labels"] = labels;
456 }
457
458 if (includeAllMetadata_)
459 {
460 const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
461
462 Json::Value metadata = Json::objectValue;
463
464 for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
465 {
466 metadata[EnumerationToString(it->first)] = it->second;
467 }
468
469 target["Metadata"] = metadata;
470 }
471 }
472
473
474 public:
475 ResourceFinder(ResourceType level,
476 bool expand) :
477 request_(level),
478 expand_(expand),
479 format_(DicomToJsonFormat_Human),
480 includeAllMetadata_(false)
481 {
482 if (expand)
483 {
484 request_.SetRetrieveMainDicomTags(level, true);
485 request_.SetRetrieveMetadata(level, true);
486 request_.SetRetrieveLabels(true);
487
488 if (level == ResourceType_Series)
489 {
490 request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
491 }
492
493 if (level == ResourceType_Instance)
494 {
495 request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
496 }
497 else
498 {
499 request_.SetRetrieveChildrenIdentifiers(true);
500 }
501
502 if (level != ResourceType_Patient)
503 {
504 request_.SetRetrieveParentIdentifier(true);
505 }
506 }
507 }
508
509 void SetRequestedTags(const std::set<DicomTag>& tags)
510 {
511 requestedTags_ = tags;
512 }
513
514 void SetFormat(DicomToJsonFormat format)
515 {
516 format_ = format;
517 }
518
519 void SetLimits(uint64_t since,
520 uint64_t count)
521 {
522 request_.SetLimits(since, count);
523 }
524
525 void SetIncludeAllMetadata(bool include)
526 {
527 includeAllMetadata_ = include;
528 }
529
530 void Execute(Json::Value& target,
531 ServerContext& context)
532 {
533 FindResponse response;
534 context.GetIndex().ExecuteFind(response, request_);
535
536 target = Json::arrayValue;
537
538 if (expand_)
539 {
540 for (size_t i = 0; i < response.GetSize(); i++)
541 {
542 Json::Value item;
543 Expand(item, response.GetResource(i));
544
545 #if 0
546 target.append(item);
547 #else
548 context.AppendFindResponse(target, request_, response.GetResource(i), format_,
549 requestedTags_, true /* allowStorageAccess */);
550 std::cout << "+++ Expected: " << target[target.size() - 1].toStyledString();
551 std::cout << "--- Actual: " << item.toStyledString();
552 #endif
553 }
554 }
555 else
556 {
557 for (size_t i = 0; i < response.GetSize(); i++)
558 {
559 target.append(response.GetResource(i).GetIdentifier());
560 }
561 }
562 }
563 };
564
565
201 template <enum ResourceType resourceType> 566 template <enum ResourceType resourceType>
202 static void ListResources(RestApiGetCall& call) 567 static void ListResources(RestApiGetCall& call)
203 { 568 {
204 if (call.IsDocumentation()) 569 if (call.IsDocumentation())
205 { 570 {
236 call.GetBooleanArgument("expand", true)); 601 call.GetBooleanArgument("expand", true));
237 602
238 std::set<DicomTag> requestedTags; 603 std::set<DicomTag> requestedTags;
239 OrthancRestApi::GetRequestedTags(requestedTags, call); 604 OrthancRestApi::GetRequestedTags(requestedTags, call);
240 605
241 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); 606 ResourceFinder finder(resourceType, expand);
242 607 finder.SetRequestedTags(requestedTags);
243 FindRequest request(resourceType); 608 finder.SetFormat(OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human));
244
245 if (expand)
246 {
247 // compatibility with default expand option
248 request.SetRetrieveMainDicomTags(resourceType, true);
249 request.SetRetrieveMetadata(resourceType, true);
250 request.SetRetrieveLabels(true);
251
252 if (resourceType == ResourceType_Series)
253 {
254 request.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
255 }
256
257 if (resourceType == ResourceType_Instance)
258 {
259 request.SetRetrieveAttachments(true); // for FileSize & FileUuid
260 }
261 else
262 {
263 request.SetRetrieveChildrenIdentifiers(true);
264 }
265
266 if (resourceType != ResourceType_Patient)
267 {
268 request.SetRetrieveParentIdentifier(true);
269 }
270 }
271 609
272 if (call.HasArgument("limit") || 610 if (call.HasArgument("limit") ||
273 call.HasArgument("since")) 611 call.HasArgument("since"))
274 { 612 {
275 if (!call.HasArgument("limit")) 613 if (!call.HasArgument("limit"))
286 call.FlattenUri()); 624 call.FlattenUri());
287 } 625 }
288 626
289 uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", "")); 627 uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", ""));
290 uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", "")); 628 uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", ""));
291 request.SetLimits(since, limit); 629 finder.SetLimits(since, limit);
292 } 630 }
293 631
294 FindResponse response; 632 Json::Value answer;
295 index.ExecuteFind(response, request); 633 finder.Execute(answer, context);
296
297 // TODO-FIND: put this in an AnswerFindResponse method !
298 Json::Value answer = Json::arrayValue;
299
300 if (expand)
301 {
302 for (size_t i = 0; i < response.GetSize(); i++)
303 {
304 context.AppendFindResponse(answer, request, response.GetResource(i), format, requestedTags, true /* allowStorageAccess */);
305 }
306 }
307 else
308 {
309 for (size_t i = 0; i < response.GetSize(); i++)
310 {
311 answer.append(response.GetResource(i).GetIdentifier());
312 }
313 }
314
315 call.GetOutput().AnswerJson(answer); 634 call.GetOutput().AnswerJson(answer);
316 } 635 }
317 else 636 else
318 { 637 {
319 /** 638 /**