Mercurial > hg > orthanc
comparison OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp @ 5137:15109c3f0f7d
added sanity checks in DicomModificationJob + automatically reconstruct resources at the end of a DicomModificationJob
author | Alain Mazy <am@osimis.io> |
---|---|
date | Wed, 18 Jan 2023 17:58:51 +0100 |
parents | e71b22a43c0b |
children | 021d7fdcb659 |
comparison
equal
deleted
inserted
replaced
5136:e71b22a43c0b | 5137:15109c3f0f7d |
---|---|
166 void ResourceModificationJob::Reset() | 166 void ResourceModificationJob::Reset() |
167 { | 167 { |
168 boost::recursive_mutex::scoped_lock lock(mutex_); | 168 boost::recursive_mutex::scoped_lock lock(mutex_); |
169 | 169 |
170 // TODO: cleanup the instances that have been generated during the previous run | 170 // TODO: cleanup the instances that have been generated during the previous run |
171 modifiedInstances_.clear(); | 171 modifiedSeries_.clear(); |
172 instancesToReconstruct_.clear(); | |
172 | 173 |
173 ThreadedSetOfInstancesJob::Reset(); | 174 ThreadedSetOfInstancesJob::Reset(); |
174 } | 175 } |
175 | 176 |
176 void ResourceModificationJob::PostProcessInstances() | 177 void ResourceModificationJob::PostProcessInstances() |
177 { | 178 { |
178 boost::recursive_mutex::scoped_lock lock(mutex_); | 179 boost::recursive_mutex::scoped_lock lock(mutex_); |
179 | 180 |
180 // reconstruct the parents MainDicomTags in case one of them has changed | 181 // reconstruct the parents MainDicomTags in case one of them has changed |
181 if (modifiedInstances_.size() > 0) | 182 if (instancesToReconstruct_.size() > 0) |
182 { | 183 { |
183 ServerContext::DicomCacheLocker locker(GetContext(), *(modifiedInstances_.begin())); | 184 for (std::set<std::string>::const_iterator it = instancesToReconstruct_.begin(); it != instancesToReconstruct_.end(); ++it) |
184 ParsedDicomFile& modifiedDicom = locker.GetDicom(); | 185 { |
185 | 186 ServerContext::DicomCacheLocker locker(GetContext(), *it); |
186 GetContext().GetIndex().ReconstructInstance(modifiedDicom); | 187 ParsedDicomFile& modifiedDicom = locker.GetDicom(); |
188 | |
189 GetContext().GetIndex().ReconstructInstance(modifiedDicom); | |
190 } | |
187 } | 191 } |
188 | 192 |
189 } | 193 } |
190 | 194 |
191 bool ResourceModificationJob::HandleInstance(const std::string& instance) | 195 bool ResourceModificationJob::HandleInstance(const std::string& instance) |
318 | 322 |
319 { | 323 { |
320 boost::recursive_mutex::scoped_lock lock(outputMutex_); | 324 boost::recursive_mutex::scoped_lock lock(outputMutex_); |
321 | 325 |
322 output_->Update(modifiedHasher); | 326 output_->Update(modifiedHasher); |
323 modifiedInstances_.insert(modifiedInstance); | 327 if (modifiedSeries_.find(modifiedHasher.HashSeries()) == modifiedSeries_.end()) |
328 { | |
329 modifiedSeries_.insert(modifiedHasher.HashSeries()); | |
330 // add an instance to reconstruct for each series | |
331 instancesToReconstruct_.insert(modifiedHasher.HashInstance()); | |
332 } | |
333 | |
324 } | 334 } |
325 | 335 |
326 return true; | 336 return true; |
327 } | 337 } |
328 | 338 |
611 | 621 |
612 if (transcode_) | 622 if (transcode_) |
613 { | 623 { |
614 value[TRANSCODE] = GetTransferSyntaxUid(transferSyntax_); | 624 value[TRANSCODE] = GetTransferSyntaxUid(transferSyntax_); |
615 } | 625 } |
616 | 626 |
617 origin_.Serialize(value[ORIGIN]); | 627 origin_.Serialize(value[ORIGIN]); |
618 | 628 |
619 Json::Value tmp; | 629 Json::Value tmp; |
620 modification_->Serialize(tmp); | 630 modification_->Serialize(tmp); |
621 value[MODIFICATION] = tmp; | 631 value[MODIFICATION] = tmp; |
628 } | 638 } |
629 | 639 |
630 return true; | 640 return true; |
631 } | 641 } |
632 } | 642 } |
643 | |
644 void ResourceModificationJob::PerformSanityChecks() | |
645 { | |
646 boost::recursive_mutex::scoped_lock lock(mutex_); // because we access the parentResources_ | |
647 | |
648 std::set<DicomTag> emptyRequestedTags; | |
649 | |
650 if (modification_.get() == NULL) | |
651 { | |
652 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
653 } | |
654 | |
655 bool replacePatientMainDicomTags = false; | |
656 bool replaceStudyMainDicomTags = false; | |
657 bool replaceSeriesMainDicomTags = false; | |
658 bool replaceInstanceMainDicomTags = false; | |
659 | |
660 ResourceType modificationLevel = modification_->GetLevel(); | |
661 std::set<DicomTag> replacedTags; | |
662 modification_->GetReplacedTags(replacedTags); | |
663 | |
664 for (std::set<DicomTag>::const_iterator it = replacedTags.begin(); it != replacedTags.end(); it++) | |
665 { | |
666 replacePatientMainDicomTags |= DicomMap::IsMainDicomTag(*it, ResourceType_Patient); | |
667 replaceStudyMainDicomTags |= DicomMap::IsMainDicomTag(*it, ResourceType_Study); | |
668 replaceSeriesMainDicomTags |= DicomMap::IsMainDicomTag(*it, ResourceType_Series); | |
669 replaceInstanceMainDicomTags |= DicomMap::IsMainDicomTag(*it, ResourceType_Instance); | |
670 } | |
671 | |
672 if (modification_->IsKept(DICOM_TAG_SOP_INSTANCE_UID) && !IsKeepSource()) | |
673 { | |
674 // note: we could refine this criteria -> this is valid only if all DicomUIDs are kept identical (but this can happen through Keep or Replace options) | |
675 throw OrthancException(ErrorCode_BadRequest, | |
676 "When keeping SOPInstanceUID tag, you must set KeepSource to true to avoid deleting the modified files at the end of the process"); | |
677 } | |
678 | |
679 if (modificationLevel == ResourceType_Study && replacePatientMainDicomTags) | |
680 { | |
681 for (std::set<std::string>::const_iterator studyId = parentResources_.begin(); studyId != parentResources_.end(); ++studyId) | |
682 { | |
683 // When modifying a study, you may not modify patient tags as you wish. | |
684 // - If this is the patient's only study, you may modify all patient tags. This could be performed in 2 steps (modify the patient and then, the study) but, | |
685 // for many use cases, it's helpful to be able to do it one step (e.g, to modify a name in a study that has just been acquired) | |
686 // - If the patient already has other studies, you may only 'attach' the study to an existing patient by modifying | |
687 // all patient tags from the study to match those of the target patient. | |
688 // - Otherwise, you can't modify the patient tags | |
689 | |
690 std::string targetPatientId; | |
691 if (modification_->IsReplaced(DICOM_TAG_PATIENT_ID)) | |
692 { | |
693 targetPatientId = modification_->GetReplacementAsString(DICOM_TAG_PATIENT_ID); | |
694 } | |
695 else | |
696 { | |
697 ExpandedResource originalStudy; | |
698 if (GetContext().GetIndex().ExpandResource(originalStudy, *studyId, ResourceType_Study, emptyRequestedTags, ExpandResourceDbFlags_IncludeMainDicomTags)) | |
699 { | |
700 targetPatientId = originalStudy.tags_.GetStringValue(DICOM_TAG_PATIENT_ID, "", false); | |
701 } | |
702 else | |
703 { | |
704 throw OrthancException(ErrorCode_UnknownResource, "Study not found"); | |
705 } | |
706 } | |
707 | |
708 // try to find the targetPatient | |
709 std::vector<std::string> lookupPatientResult; | |
710 GetContext().GetIndex().LookupIdentifierExact(lookupPatientResult, ResourceType_Patient, DICOM_TAG_PATIENT_ID, targetPatientId); | |
711 | |
712 // if the patient exists, check how many child studies it has. | |
713 if (lookupPatientResult.size() >= 1) | |
714 { | |
715 ExpandedResource targetPatient; | |
716 | |
717 if (GetContext().GetIndex().ExpandResource(targetPatient, lookupPatientResult[0], ResourceType_Patient, emptyRequestedTags, static_cast<ExpandResourceDbFlags>(ExpandResourceDbFlags_IncludeMainDicomTags | ExpandResourceDbFlags_IncludeChildren))) | |
718 { | |
719 const std::list<std::string> childrenIds = targetPatient.childrenIds_; | |
720 bool targetPatientHasOtherStudies = childrenIds.size() > 1; | |
721 if (childrenIds.size() == 1) | |
722 { | |
723 targetPatientHasOtherStudies = std::find(childrenIds.begin(), childrenIds.end(), *studyId) == childrenIds.end(); // if the patient has one study that is not the one being modified | |
724 } | |
725 | |
726 if (targetPatientHasOtherStudies) | |
727 { | |
728 // this is allowed if all patient replacedTags do match the target patient tags | |
729 DicomMap targetPatientTags; | |
730 targetPatient.tags_.ExtractPatientInformation(targetPatientTags); | |
731 | |
732 for (std::set<DicomTag>::const_iterator mainPatientTag = DicomMap::GetMainDicomTags(ResourceType_Patient).begin(); | |
733 mainPatientTag != DicomMap::GetMainDicomTags(ResourceType_Patient).end(); ++mainPatientTag) | |
734 { | |
735 if (targetPatientTags.HasTag(*mainPatientTag) | |
736 && (!modification_->IsReplaced(*mainPatientTag) | |
737 || modification_->GetReplacementAsString(*mainPatientTag) != targetPatientTags.GetStringValue(*mainPatientTag, "", false))) | |
738 { | |
739 throw OrthancException(ErrorCode_BadRequest, std::string("Trying to change patient tags in a study. The Patient already exists and has other studies. All the 'Replace' tags should match the existing patient main dicom tags. Try using /patients/../modify instead to modify the patient. Failing tag: ") + mainPatientTag->Format()); | |
740 } | |
741 else if (!targetPatientTags.HasTag(*mainPatientTag) && modification_->IsReplaced(*mainPatientTag) ) | |
742 { | |
743 throw OrthancException(ErrorCode_BadRequest, std::string("Trying to change patient tags in a study. The Patient already exists and has other studies. You are trying to replace a tag that is not defined yet in this patient. Try using /patients/../modify instead to modify the patient. Failing tag: ") + mainPatientTag->Format()); | |
744 } | |
745 } | |
746 } | |
747 // TODO: need reconstruct_ | |
748 } | |
749 } | |
750 } | |
751 | |
752 } | |
753 } | |
633 } | 754 } |