Mercurial > hg > orthanc
comparison OrthancServer/Sources/ServerJobs/ArchiveJob.cpp @ 5401:fc604681e6be
When exporting a study archive, make sure to use the PatientName from the study and not from the patient in case of PatientID collision
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 16 Oct 2023 17:30:40 +0200 |
parents | b5f2122a1334 |
children | 05cb668c5f3f |
comparison
equal
deleted
inserted
replaced
5400:54bed7c93d6a | 5401:fc604681e6be |
---|---|
304 } | 304 } |
305 } | 305 } |
306 } | 306 } |
307 }; | 307 }; |
308 | 308 |
309 // This enum defines specific resource types to be used when exporting the archive. | |
310 // It defines if we should use the PatientInfo from the Patient or from the Study. | |
311 enum ArchiveResourceType | |
312 { | |
313 ArchiveResourceType_Patient = 0, | |
314 ArchiveResourceType_PatientInfoFromStudy = 1, | |
315 ArchiveResourceType_Study = 2, | |
316 ArchiveResourceType_Series = 3, | |
317 ArchiveResourceType_Instance = 4 | |
318 }; | |
319 | |
320 ResourceType GetResourceIdType(ArchiveResourceType type) | |
321 { | |
322 switch (type) | |
323 { | |
324 case ArchiveResourceType_Patient: | |
325 return ResourceType_Patient; | |
326 case ArchiveResourceType_PatientInfoFromStudy: // get the Patient tags from the Study id | |
327 return ResourceType_Study; | |
328 case ArchiveResourceType_Study: | |
329 return ResourceType_Study; | |
330 case ArchiveResourceType_Series: | |
331 return ResourceType_Series; | |
332 case ArchiveResourceType_Instance: | |
333 return ResourceType_Instance; | |
334 default: | |
335 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
336 } | |
337 } | |
338 | |
339 ResourceType GetResourceLevel(ArchiveResourceType type) | |
340 { | |
341 switch (type) | |
342 { | |
343 case ArchiveResourceType_Patient: | |
344 return ResourceType_Patient; | |
345 case ArchiveResourceType_PatientInfoFromStudy: // this is actually the same level as the Patient | |
346 return ResourceType_Patient; | |
347 case ArchiveResourceType_Study: | |
348 return ResourceType_Study; | |
349 case ArchiveResourceType_Series: | |
350 return ResourceType_Series; | |
351 case ArchiveResourceType_Instance: | |
352 return ResourceType_Instance; | |
353 default: | |
354 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
355 } | |
356 } | |
357 | |
358 ArchiveResourceType GetArchiveResourceType(ResourceType type) | |
359 { | |
360 switch (type) | |
361 { | |
362 case ResourceType_Patient: | |
363 return ArchiveResourceType_Patient; | |
364 case ArchiveResourceType_Study: | |
365 return ArchiveResourceType_PatientInfoFromStudy; | |
366 case ResourceType_Series: | |
367 return ArchiveResourceType_Series; | |
368 case ResourceType_Instance: | |
369 return ArchiveResourceType_Instance; | |
370 default: | |
371 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
372 } | |
373 } | |
374 | |
375 ArchiveResourceType GetChildResourceType(ArchiveResourceType type) | |
376 { | |
377 switch (type) | |
378 { | |
379 case ArchiveResourceType_Patient: | |
380 case ArchiveResourceType_PatientInfoFromStudy: | |
381 return ArchiveResourceType_Study; | |
382 | |
383 case ArchiveResourceType_Study: | |
384 return ArchiveResourceType_Series; | |
385 | |
386 case ArchiveResourceType_Series: | |
387 return ArchiveResourceType_Instance; | |
388 | |
389 default: | |
390 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
391 } | |
392 } | |
393 | |
309 | 394 |
310 class ArchiveJob::ResourceIdentifiers : public boost::noncopyable | 395 class ArchiveJob::ResourceIdentifiers : public boost::noncopyable |
311 { | 396 { |
312 private: | 397 private: |
313 ResourceType level_; | 398 ResourceType level_; |
408 public: | 493 public: |
409 virtual ~IArchiveVisitor() | 494 virtual ~IArchiveVisitor() |
410 { | 495 { |
411 } | 496 } |
412 | 497 |
413 virtual void Open(ResourceType level, | 498 virtual void Open(ArchiveResourceType level, |
414 const std::string& publicId) = 0; | 499 const std::string& publicId) = 0; |
415 | 500 |
416 virtual void Close() = 0; | 501 virtual void Close() = 0; |
417 | 502 |
418 virtual void AddInstance(const std::string& instanceId, | 503 virtual void AddInstance(const std::string& instanceId, |
437 }; | 522 }; |
438 | 523 |
439 // A "NULL" value for ArchiveIndex indicates a non-expanded node | 524 // A "NULL" value for ArchiveIndex indicates a non-expanded node |
440 typedef std::map<std::string, ArchiveIndex*> Resources; | 525 typedef std::map<std::string, ArchiveIndex*> Resources; |
441 | 526 |
442 ResourceType level_; | 527 ArchiveResourceType level_; |
443 Resources resources_; // Only at patient/study/series level | 528 Resources resources_; // Only at patient/study/series level |
444 std::list<Instance> instances_; // Only at instance level | 529 std::list<Instance> instances_; // Only at instance level |
445 | 530 |
446 | 531 |
447 void AddResourceToExpand(ServerIndex& index, | 532 void AddResourceToExpand(ServerIndex& index, |
448 const std::string& id) | 533 const std::string& id) |
449 { | 534 { |
450 if (level_ == ResourceType_Instance) | 535 if (level_ == ArchiveResourceType_Instance) |
451 { | 536 { |
452 FileInfo tmp; | 537 FileInfo tmp; |
453 int64_t revision; // ignored | 538 int64_t revision; // ignored |
454 if (index.LookupAttachment(tmp, revision, id, FileContentType_Dicom)) | 539 if (index.LookupAttachment(tmp, revision, id, FileContentType_Dicom)) |
455 { | 540 { |
462 } | 547 } |
463 } | 548 } |
464 | 549 |
465 | 550 |
466 public: | 551 public: |
467 explicit ArchiveIndex(ResourceType level) : | 552 explicit ArchiveIndex(ArchiveResourceType level) : |
468 level_(level) | 553 level_(level) |
469 { | 554 { |
470 } | 555 } |
471 | 556 |
472 ~ArchiveIndex() | 557 ~ArchiveIndex() |
480 | 565 |
481 | 566 |
482 void Add(ServerIndex& index, | 567 void Add(ServerIndex& index, |
483 const ResourceIdentifiers& resource) | 568 const ResourceIdentifiers& resource) |
484 { | 569 { |
485 const std::string& id = resource.GetIdentifier(level_); | 570 const std::string& id = resource.GetIdentifier(GetResourceIdType(level_)); |
486 Resources::iterator previous = resources_.find(id); | 571 Resources::iterator previous = resources_.find(id); |
487 | 572 |
488 if (level_ == ResourceType_Instance) | 573 if (level_ == ArchiveResourceType_Instance) |
489 { | 574 { |
490 AddResourceToExpand(index, id); | 575 AddResourceToExpand(index, id); |
491 } | 576 } |
492 else if (resource.GetLevel() == level_) | 577 else if (resource.GetLevel() == GetResourceLevel(level_)) |
493 { | 578 { |
494 // Mark this resource for further expansion | 579 // Mark this resource for further expansion |
495 if (previous != resources_.end()) | 580 if (previous != resources_.end()) |
496 { | 581 { |
497 delete previous->second; | 582 delete previous->second; |
517 } | 602 } |
518 | 603 |
519 | 604 |
520 void Expand(ServerIndex& index) | 605 void Expand(ServerIndex& index) |
521 { | 606 { |
522 if (level_ == ResourceType_Instance) | 607 if (level_ == ArchiveResourceType_Instance) |
523 { | 608 { |
524 // Expanding an instance node makes no sense | 609 // Expanding an instance node makes no sense |
525 return; | 610 return; |
526 } | 611 } |
527 | 612 |
551 } | 636 } |
552 | 637 |
553 | 638 |
554 void Apply(IArchiveVisitor& visitor) const | 639 void Apply(IArchiveVisitor& visitor) const |
555 { | 640 { |
556 if (level_ == ResourceType_Instance) | 641 if (level_ == ArchiveResourceType_Instance) |
557 { | 642 { |
558 for (std::list<Instance>::const_iterator | 643 for (std::list<Instance>::const_iterator |
559 it = instances_.begin(); it != instances_.end(); ++it) | 644 it = instances_.begin(); it != instances_.end(); ++it) |
560 { | 645 { |
561 visitor.AddInstance(it->id_, it->uncompressedSize_); | 646 visitor.AddInstance(it->id_, it->uncompressedSize_); |
821 } | 906 } |
822 | 907 |
823 snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%%08d.dcm"); | 908 snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%%08d.dcm"); |
824 } | 909 } |
825 | 910 |
826 virtual void Open(ResourceType level, | 911 virtual void Open(ArchiveResourceType level, |
827 const std::string& publicId) ORTHANC_OVERRIDE | 912 const std::string& publicId) ORTHANC_OVERRIDE |
828 { | 913 { |
829 std::string path; | 914 std::string path; |
830 | 915 |
831 DicomMap tags; | 916 DicomMap tags; |
832 if (context_.GetIndex().GetMainDicomTags(tags, publicId, level, level)) | 917 ResourceType resourceIdLevel = GetResourceIdType(level); |
918 ResourceType interestLevel = (level == ArchiveResourceType_PatientInfoFromStudy ? ResourceType_Patient : resourceIdLevel); | |
919 | |
920 if (context_.GetIndex().GetMainDicomTags(tags, publicId, resourceIdLevel, interestLevel)) | |
833 { | 921 { |
834 switch (level) | 922 switch (level) |
835 { | 923 { |
836 case ResourceType_Patient: | 924 case ArchiveResourceType_Patient: |
925 case ArchiveResourceType_PatientInfoFromStudy: | |
837 path = GetTag(tags, DICOM_TAG_PATIENT_ID) + " " + GetTag(tags, DICOM_TAG_PATIENT_NAME); | 926 path = GetTag(tags, DICOM_TAG_PATIENT_ID) + " " + GetTag(tags, DICOM_TAG_PATIENT_NAME); |
838 break; | 927 break; |
839 | 928 |
840 case ResourceType_Study: | 929 case ArchiveResourceType_Study: |
841 path = GetTag(tags, DICOM_TAG_ACCESSION_NUMBER) + " " + GetTag(tags, DICOM_TAG_STUDY_DESCRIPTION); | 930 path = GetTag(tags, DICOM_TAG_ACCESSION_NUMBER) + " " + GetTag(tags, DICOM_TAG_STUDY_DESCRIPTION); |
842 break; | 931 break; |
843 | 932 |
844 case ResourceType_Series: | 933 case ArchiveResourceType_Series: |
845 { | 934 { |
846 std::string modality = GetTag(tags, DICOM_TAG_MODALITY); | 935 std::string modality = GetTag(tags, DICOM_TAG_MODALITY); |
847 path = modality + " " + GetTag(tags, DICOM_TAG_SERIES_DESCRIPTION); | 936 path = modality + " " + GetTag(tags, DICOM_TAG_SERIES_DESCRIPTION); |
848 | 937 |
849 if (modality.size() == 0) | 938 if (modality.size() == 0) |
873 | 962 |
874 path = Toolbox::StripSpaces(Toolbox::ConvertToAscii(path)); | 963 path = Toolbox::StripSpaces(Toolbox::ConvertToAscii(path)); |
875 | 964 |
876 if (path.empty()) | 965 if (path.empty()) |
877 { | 966 { |
878 path = std::string("Unknown ") + EnumerationToString(level); | 967 path = std::string("Unknown ") + EnumerationToString(GetResourceLevel(level)); |
879 } | 968 } |
880 | 969 |
881 commands_.AddOpenDirectory(path.c_str()); | 970 commands_.AddOpenDirectory(path.c_str()); |
882 } | 971 } |
883 | 972 |
909 commands_(commands), | 998 commands_(commands), |
910 counter_(0) | 999 counter_(0) |
911 { | 1000 { |
912 } | 1001 } |
913 | 1002 |
914 virtual void Open(ResourceType level, | 1003 virtual void Open(ArchiveResourceType level, |
915 const std::string& publicId) ORTHANC_OVERRIDE | 1004 const std::string& publicId) ORTHANC_OVERRIDE |
916 { | 1005 { |
917 } | 1006 } |
918 | 1007 |
919 virtual void Close() ORTHANC_OVERRIDE | 1008 virtual void Close() ORTHANC_OVERRIDE |
1100 }; | 1189 }; |
1101 | 1190 |
1102 | 1191 |
1103 ArchiveJob::ArchiveJob(ServerContext& context, | 1192 ArchiveJob::ArchiveJob(ServerContext& context, |
1104 bool isMedia, | 1193 bool isMedia, |
1105 bool enableExtendedSopClass) : | 1194 bool enableExtendedSopClass, |
1195 ResourceType jobLevel) : | |
1106 context_(context), | 1196 context_(context), |
1107 archive_(new ArchiveIndex(ResourceType_Patient)), // root | 1197 archive_(new ArchiveIndex(GetArchiveResourceType(jobLevel))), // root |
1108 isMedia_(isMedia), | 1198 isMedia_(isMedia), |
1109 enableExtendedSopClass_(enableExtendedSopClass), | 1199 enableExtendedSopClass_(enableExtendedSopClass), |
1110 currentStep_(0), | 1200 currentStep_(0), |
1111 instancesCount_(0), | 1201 instancesCount_(0), |
1112 uncompressedSize_(0), | 1202 uncompressedSize_(0), |