# HG changeset patch # User Sebastien Jodogne # Date 1589818819 -7200 # Node ID 771dbd9eb3bd40c9af8033289194a6050c52adcc # Parent 3661e2a72482d294bfee7c4939991f09efdf879d class CleaningInstancesJob to share cleaning code by merge/split jobs diff -r 3661e2a72482 -r 771dbd9eb3bd CMakeLists.txt --- a/CMakeLists.txt Mon May 18 17:41:05 2020 +0200 +++ b/CMakeLists.txt Mon May 18 18:20:19 2020 +0200 @@ -90,6 +90,7 @@ OrthancServer/ServerEnumerations.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerJobs/ArchiveJob.cpp + OrthancServer/ServerJobs/CleaningInstancesJob.cpp OrthancServer/ServerJobs/DicomModalityStoreJob.cpp OrthancServer/ServerJobs/DicomMoveScuJob.cpp OrthancServer/ServerJobs/LuaJobManager.cpp diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Mon May 18 17:41:05 2020 +0200 +++ b/OrthancServer/ServerContext.cpp Mon May 18 18:20:19 2020 +0200 @@ -295,7 +295,7 @@ } else { - isIngestTranscoding_ = true; + isIngestTranscoding_ = false; LOG(INFO) << "Automated transcoding of incoming DICOM instances is disabled"; } } @@ -545,7 +545,7 @@ { if (!isIngestTranscoding_) { - // No automated transcoding + // No automated transcoding. This was the only path in Orthanc <= 1.6.1. return StoreAfterTranscoding(resultPublicId, dicom, mode); } else diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/CleaningInstancesJob.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/ServerJobs/CleaningInstancesJob.cpp Mon May 18 18:20:19 2020 +0200 @@ -0,0 +1,120 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "CleaningInstancesJob.h" + +#include "../../Core/SerializationToolbox.h" +#include "../ServerContext.h" + + +namespace Orthanc +{ + bool CleaningInstancesJob::HandleTrailingStep() + { + if (!keepSource_) + { + const size_t n = GetInstancesCount(); + + for (size_t i = 0; i < n; i++) + { + Json::Value tmp; + context_.DeleteResource(tmp, GetInstance(i), ResourceType_Instance); + } + } + + return true; + } + + + void CleaningInstancesJob::SetKeepSource(bool keep) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + keepSource_ = keep; + } + + + static const char* KEEP_SOURCE = "KeepSource"; + + + CleaningInstancesJob::CleaningInstancesJob(ServerContext& context, + const Json::Value& serialized, + bool defaultKeepSource) : + SetOfInstancesJob(serialized), // (*) + context_(context) + { + if (!HasTrailingStep()) + { + // Should have been set by (*) + throw OrthancException(ErrorCode_InternalError); + } + + if (serialized.isMember(KEEP_SOURCE)) + { + keepSource_ = SerializationToolbox::ReadBoolean(serialized, KEEP_SOURCE); + } + else + { + keepSource_ = defaultKeepSource; + } + } + + + bool CleaningInstancesJob::Serialize(Json::Value& target) + { + if (!SetOfInstancesJob::Serialize(target)) + { + return false; + } + else + { + target[KEEP_SOURCE] = keepSource_; + return true; + } + } + + + void CleaningInstancesJob::Start() + { + if (!HasTrailingStep()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls, + "AddTrailingStep() should have been called before submitting the job"); + } + + SetOfInstancesJob::Start(); + } +} diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/CleaningInstancesJob.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/ServerJobs/CleaningInstancesJob.h Mon May 18 18:20:19 2020 +0200 @@ -0,0 +1,79 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Core/JobsEngine/SetOfInstancesJob.h" + +namespace Orthanc +{ + class ServerContext; + + class CleaningInstancesJob : public SetOfInstancesJob + { + private: + ServerContext& context_; + bool keepSource_; + + protected: + virtual bool HandleTrailingStep(); + + public: + CleaningInstancesJob(ServerContext& context, + bool keepSource) : + context_(context), + keepSource_(keepSource) + { + } + + CleaningInstancesJob(ServerContext& context, + const Json::Value& serialized, + bool defaultKeepSource); + + ServerContext& GetContext() const + { + return context_; + } + + bool IsKeepSource() const + { + return keepSource_; + } + + void SetKeepSource(bool keep); + + virtual bool Serialize(Json::Value& target); + + virtual void Start(); + }; +} diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/MergeStudyJob.cpp --- a/OrthancServer/ServerJobs/MergeStudyJob.cpp Mon May 18 17:41:05 2020 +0200 +++ b/OrthancServer/ServerJobs/MergeStudyJob.cpp Mon May 18 18:20:19 2020 +0200 @@ -48,7 +48,7 @@ // Add all the instances of the series as to be processed std::list instances; - context_.GetIndex().GetChildren(instances, series); + GetContext().GetIndex().GetChildren(instances, series); for (std::list::const_iterator it = instances.begin(); it != instances.end(); ++it) @@ -68,7 +68,7 @@ else { std::list series; - context_.GetIndex().GetChildren(series, study); + GetContext().GetIndex().GetChildren(series, study); for (std::list::const_iterator it = series.begin(); it != series.end(); ++it) @@ -95,7 +95,7 @@ try { - ServerContext::DicomCacheLocker locker(context_, instance); + ServerContext::DicomCacheLocker locker(GetContext(), instance); modified.reset(locker.GetDicom().Clone(true)); } catch (OrthancException&) @@ -151,7 +151,7 @@ toStore.SetParsedDicomFile(*modified); std::string modifiedInstance; - if (context_.Store(modifiedInstance, toStore, + if (GetContext().Store(modifiedInstance, toStore, StoreInstanceMode_Default) != StoreStatus_Success) { LOG(ERROR) << "Error while storing a modified instance " << instance; @@ -162,27 +162,9 @@ } - bool MergeStudyJob::HandleTrailingStep() - { - if (!keepSource_) - { - const size_t n = GetInstancesCount(); - - for (size_t i = 0; i < n; i++) - { - Json::Value tmp; - context_.DeleteResource(tmp, GetInstance(i), ResourceType_Instance); - } - } - - return true; - } - - MergeStudyJob::MergeStudyJob(ServerContext& context, const std::string& targetStudy) : - context_(context), - keepSource_(false), + CleaningInstancesJob(context, false /* by default, remove source instances */), targetStudy_(targetStudy) { /** @@ -191,7 +173,7 @@ ResourceType type; - if (!context_.GetIndex().LookupResourceType(type, targetStudy) || + if (!GetContext().GetIndex().LookupResourceType(type, targetStudy) || type != ResourceType_Study) { throw OrthancException(ErrorCode_UnknownResource, @@ -208,7 +190,7 @@ DicomTag::AddTagsForModule(removals_, DicomModule_Study); std::list instances; - context_.GetIndex().GetChildInstances(instances, targetStudy); + GetContext().GetIndex().GetChildInstances(instances, targetStudy); if (instances.empty()) { @@ -218,7 +200,7 @@ DicomMap dicom; { - ServerContext::DicomCacheLocker locker(context_, instances.front()); + ServerContext::DicomCacheLocker locker(GetContext(), instances.front()); locker.GetDicom().ExtractDicomSummary(dicom); } @@ -266,7 +248,7 @@ { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else if (!context_.GetIndex().LookupResourceType(level, studyOrSeries)) + else if (!GetContext().GetIndex().LookupResourceType(level, studyOrSeries)) { throw OrthancException(ErrorCode_UnknownResource, "Cannot find this resource: " + studyOrSeries); @@ -301,7 +283,7 @@ { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else if (!context_.GetIndex().LookupParent(parent, series, ResourceType_Study)) + else if (!GetContext().GetIndex().LookupParent(parent, series, ResourceType_Study)) { throw OrthancException(ErrorCode_UnknownResource, "This resource is not a series: " + series); @@ -327,7 +309,7 @@ { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else if (!context_.GetIndex().LookupResourceType(actualLevel, study) || + else if (!GetContext().GetIndex().LookupResourceType(actualLevel, study) || actualLevel != ResourceType_Study) { throw OrthancException(ErrorCode_UnknownResource, @@ -340,25 +322,13 @@ } - void MergeStudyJob::SetKeepSource(bool keep) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - keepSource_ = keep; - } - - void MergeStudyJob::GetPublicContent(Json::Value& value) { - SetOfInstancesJob::GetPublicContent(value); + CleaningInstancesJob::GetPublicContent(value); value["TargetStudy"] = targetStudy_; } - static const char* KEEP_SOURCE = "KeepSource"; static const char* TARGET_STUDY = "TargetStudy"; static const char* REPLACEMENTS = "Replacements"; static const char* REMOVALS = "Removals"; @@ -368,8 +338,8 @@ MergeStudyJob::MergeStudyJob(ServerContext& context, const Json::Value& serialized) : - SetOfInstancesJob(serialized), // (*) - context_(context) + CleaningInstancesJob(context, serialized, + false /* by default, remove source instances */) // (*) { if (!HasTrailingStep()) { @@ -377,7 +347,6 @@ throw OrthancException(ErrorCode_InternalError); } - keepSource_ = SerializationToolbox::ReadBoolean(serialized, KEEP_SOURCE); targetStudy_ = SerializationToolbox::ReadString(serialized, TARGET_STUDY); SerializationToolbox::ReadMapOfTags(replacements_, serialized, REPLACEMENTS); SerializationToolbox::ReadSetOfTags(removals_, serialized, REMOVALS); @@ -388,13 +357,12 @@ bool MergeStudyJob::Serialize(Json::Value& target) { - if (!SetOfInstancesJob::Serialize(target)) + if (!CleaningInstancesJob::Serialize(target)) { return false; } else { - target[KEEP_SOURCE] = keepSource_; target[TARGET_STUDY] = targetStudy_; SerializationToolbox::WriteMapOfTags(target, replacements_, REPLACEMENTS); SerializationToolbox::WriteSetOfTags(target, removals_, REMOVALS); diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/MergeStudyJob.h --- a/OrthancServer/ServerJobs/MergeStudyJob.h Mon May 18 17:41:05 2020 +0200 +++ b/OrthancServer/ServerJobs/MergeStudyJob.h Mon May 18 18:20:19 2020 +0200 @@ -34,22 +34,20 @@ #pragma once #include "../../Core/DicomFormat/DicomMap.h" -#include "../../Core/JobsEngine/SetOfInstancesJob.h" #include "../DicomInstanceOrigin.h" +#include "CleaningInstancesJob.h" namespace Orthanc { class ServerContext; - class MergeStudyJob : public SetOfInstancesJob + class MergeStudyJob : public CleaningInstancesJob { private: typedef std::map SeriesUidMap; typedef std::map Replacements; - ServerContext& context_; - bool keepSource_; std::string targetStudy_; Replacements replacements_; std::set removals_; @@ -61,12 +59,9 @@ void AddSourceStudyInternal(const std::string& study); - protected: virtual bool HandleInstance(const std::string& instance); - virtual bool HandleTrailingStep(); - public: MergeStudyJob(ServerContext& context, const std::string& targetStudy); @@ -85,13 +80,6 @@ void AddSourceSeries(const std::string& series); - bool IsKeepSource() const - { - return keepSource_; - } - - void SetKeepSource(bool keep); - void SetOrigin(const DicomInstanceOrigin& origin); void SetOrigin(const RestApiCall& call); diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/SplitStudyJob.cpp --- a/OrthancServer/ServerJobs/SplitStudyJob.cpp Mon May 18 17:41:05 2020 +0200 +++ b/OrthancServer/ServerJobs/SplitStudyJob.cpp Mon May 18 18:20:19 2020 +0200 @@ -81,7 +81,7 @@ try { - ServerContext::DicomCacheLocker locker(context_, instance); + ServerContext::DicomCacheLocker locker(GetContext(), instance); modified.reset(locker.GetDicom().Clone(true)); } catch (OrthancException&) @@ -144,8 +144,8 @@ toStore.SetParsedDicomFile(*modified); std::string modifiedInstance; - if (context_.Store(modifiedInstance, toStore, - StoreInstanceMode_Default) != StoreStatus_Success) + if (GetContext().Store(modifiedInstance, toStore, + StoreInstanceMode_Default) != StoreStatus_Success) { LOG(ERROR) << "Error while storing a modified instance " << instance; return false; @@ -155,27 +155,9 @@ } - bool SplitStudyJob::HandleTrailingStep() - { - if (!keepSource_) - { - const size_t n = GetInstancesCount(); - - for (size_t i = 0; i < n; i++) - { - Json::Value tmp; - context_.DeleteResource(tmp, GetInstance(i), ResourceType_Instance); - } - } - - return true; - } - - SplitStudyJob::SplitStudyJob(ServerContext& context, const std::string& sourceStudy) : - context_(context), - keepSource_(false), + CleaningInstancesJob(context, false /* by default, remove source instances */), sourceStudy_(sourceStudy), targetStudyUid_(FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)) { @@ -183,7 +165,7 @@ ResourceType type; - if (!context_.GetIndex().LookupResourceType(type, sourceStudy) || + if (!GetContext().GetIndex().LookupResourceType(type, sourceStudy) || type != ResourceType_Study) { throw OrthancException(ErrorCode_UnknownResource, @@ -219,7 +201,7 @@ { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else if (!context_.GetIndex().LookupParent(parent, series, ResourceType_Study) || + else if (!GetContext().GetIndex().LookupParent(parent, series, ResourceType_Study) || parent != sourceStudy_) { throw OrthancException(ErrorCode_UnknownResource, @@ -232,7 +214,7 @@ // Add all the instances of the series as to be processed std::list instances; - context_.GetIndex().GetChildren(instances, series); + GetContext().GetIndex().GetChildren(instances, series); for (std::list::const_iterator it = instances.begin(); it != instances.end(); ++it) @@ -243,17 +225,6 @@ } - void SplitStudyJob::SetKeepSource(bool keep) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - keepSource_ = keep; - } - - bool SplitStudyJob::LookupTargetSeriesUid(std::string& uid, const std::string& series) const { @@ -315,7 +286,7 @@ void SplitStudyJob::GetPublicContent(Json::Value& value) { - SetOfInstancesJob::GetPublicContent(value); + CleaningInstancesJob::GetPublicContent(value); if (!targetStudy_.empty()) { @@ -326,7 +297,6 @@ } - static const char* KEEP_SOURCE = "KeepSource"; static const char* SOURCE_STUDY = "SourceStudy"; static const char* TARGET_STUDY = "TargetStudy"; static const char* TARGET_STUDY_UID = "TargetStudyUID"; @@ -338,8 +308,8 @@ SplitStudyJob::SplitStudyJob(ServerContext& context, const Json::Value& serialized) : - SetOfInstancesJob(serialized), // (*) - context_(context) + CleaningInstancesJob(context, serialized, + false /* by default, remove source instances */) // (*) { if (!HasTrailingStep()) { @@ -349,7 +319,6 @@ Setup(); - keepSource_ = SerializationToolbox::ReadBoolean(serialized, KEEP_SOURCE); sourceStudy_ = SerializationToolbox::ReadString(serialized, SOURCE_STUDY); targetStudy_ = SerializationToolbox::ReadString(serialized, TARGET_STUDY); targetStudyUid_ = SerializationToolbox::ReadString(serialized, TARGET_STUDY_UID); @@ -362,13 +331,12 @@ bool SplitStudyJob::Serialize(Json::Value& target) { - if (!SetOfInstancesJob::Serialize(target)) + if (!CleaningInstancesJob::Serialize(target)) { return false; } else { - target[KEEP_SOURCE] = keepSource_; target[SOURCE_STUDY] = sourceStudy_; target[TARGET_STUDY] = targetStudy_; target[TARGET_STUDY_UID] = targetStudyUid_; diff -r 3661e2a72482 -r 771dbd9eb3bd OrthancServer/ServerJobs/SplitStudyJob.h --- a/OrthancServer/ServerJobs/SplitStudyJob.h Mon May 18 17:41:05 2020 +0200 +++ b/OrthancServer/ServerJobs/SplitStudyJob.h Mon May 18 18:20:19 2020 +0200 @@ -33,24 +33,22 @@ #pragma once -#include "../../Core/JobsEngine/SetOfInstancesJob.h" #include "../../Core/DicomFormat/DicomTag.h" #include "../DicomInstanceOrigin.h" +#include "CleaningInstancesJob.h" namespace Orthanc { class ServerContext; - class SplitStudyJob : public SetOfInstancesJob + class SplitStudyJob : public CleaningInstancesJob { private: typedef std::map SeriesUidMap; typedef std::map Replacements; - ServerContext& context_; std::set allowedTags_; - bool keepSource_; std::string sourceStudy_; std::string targetStudy_; std::string targetStudyUid_; @@ -66,8 +64,6 @@ protected: virtual bool HandleInstance(const std::string& instance); - virtual bool HandleTrailingStep(); - public: SplitStudyJob(ServerContext& context, const std::string& sourceStudy); @@ -92,13 +88,6 @@ void AddSourceSeries(const std::string& series); - bool IsKeepSource() const - { - return keepSource_; - } - - void SetKeepSource(bool keep); - bool LookupTargetSeriesUid(std::string& uid, const std::string& series) const;