Mercurial > hg > orthanc
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 /** |