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),