Mercurial > hg > orthanc
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 { |