changeset 5495:4b3f5986eca1

Added a 'KeepLabels' option in /modify routes (default = false)
author Alain Mazy <am@osimis.io>
date Mon, 22 Jan 2024 17:14:11 +0100
parents 42e6593aa78e
children e4294feb0a3a
files NEWS OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake OrthancFramework/Sources/DicomParsing/DicomModification.cpp OrthancFramework/Sources/DicomParsing/DicomModification.h OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp
diffstat 8 files changed, 80 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Jan 10 07:21:50 2024 +0100
+++ b/NEWS	Mon Jan 22 17:14:11 2024 +0100
@@ -1,6 +1,12 @@
 Pending changes in the mainline
 ===============================
 
+REST API
+--------
+
+* API version upgraded to 23
+* Added a 'KeepLabels' option in /modify routes (default = false)
+
 Maintenance
 -----------
 
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake	Mon Jan 22 17:14:11 2024 +0100
@@ -38,7 +38,7 @@
 # Version of the Orthanc API, can be retrieved from "/system" URI in
 # order to check whether new URI endpoints are available even if using
 # the mainline version of Orthanc
-set(ORTHANC_API_VERSION "22")
+set(ORTHANC_API_VERSION "23")
 
 
 #####################################################################
--- a/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Mon Jan 22 17:14:11 2024 +0100
@@ -525,6 +525,7 @@
   
   DicomModification::DicomModification() :
     removePrivateTags_(false),
+    keepLabels_(false),
     level_(ResourceType_Instance),
     allowManualIdentifiers_(true),
     keepStudyInstanceUid_(false),
@@ -692,6 +693,16 @@
     return removePrivateTags_;
   }
 
+  void DicomModification::SetKeepLabels(bool keep)
+  {
+    keepLabels_ = keep;
+  }
+
+  bool DicomModification::AreLabelsKept() const
+  {
+    return keepLabels_;
+  }
+
   void DicomModification::SetLevel(ResourceType level)
   {
     uidMap_.clear();
@@ -1273,6 +1284,11 @@
       SetRemovePrivateTags(true);
     }
 
+    if (GetBooleanValue("KeepLabels", request, false))
+    {
+      SetKeepLabels(true);
+    }
+
     if (request.isMember("Remove"))
     {
       ParseListOfTags(*this, request["Remove"], TagOperation_Remove, force);
@@ -1391,6 +1407,11 @@
       SetRemovePrivateTags(false);
     }
 
+    if (GetBooleanValue("KeepLabels", request, false))
+    {
+      SetKeepLabels(true);
+    }
+
     if (request.isMember("Remove"))
     {
       ParseListOfTags(*this, request["Remove"], TagOperation_Remove, force);
--- a/OrthancFramework/Sources/DicomParsing/DicomModification.h	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DicomModification.h	Mon Jan 22 17:14:11 2024 +0100
@@ -131,6 +131,7 @@
     SetOfTags keep_;
     Replacements replacements_;
     bool removePrivateTags_;
+    bool keepLabels_;
     ResourceType level_;
     UidMap uidMap_;
     SetOfTags privateTagsToKeep_;
@@ -223,6 +224,10 @@
 
     bool ArePrivateTagsRemoved() const;
 
+    void SetKeepLabels(bool keep);
+
+    bool AreLabelsKept() const;
+
     void SetLevel(ResourceType level);
 
     ResourceType GetLevel() const;
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Mon Jan 22 17:14:11 2024 +0100
@@ -3620,6 +3620,17 @@
   }
   
 
+  void StatelessDatabaseOperations::AddLabels(const std::string& publicId,
+                                              ResourceType level,
+                                              const std::set<std::string>& labels)
+  {
+    for (std::set<std::string>::const_iterator it = labels.begin(); it != labels.end(); ++it)
+    {
+      ModifyLabel(publicId, level, *it, LabelOperation_Add);
+    }
+  }
+
+
   void StatelessDatabaseOperations::ModifyLabel(const std::string& publicId,
                                                 ResourceType level,
                                                 const std::string& label,
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Mon Jan 22 17:14:11 2024 +0100
@@ -785,6 +785,10 @@
                      const std::string& label,
                      LabelOperation operation);
 
+    void AddLabels(const std::string& publicId,
+                   ResourceType level,
+                   const std::set<std::string>& labels);
+
     bool HasLabelsSupport();
   };
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Mon Jan 22 17:14:11 2024 +0100
@@ -46,6 +46,7 @@
 static const char* const INTERPRET_BINARY_TAGS = "InterpretBinaryTags";
 static const char* const KEEP = "Keep";
 static const char* const KEEP_PRIVATE_TAGS = "KeepPrivateTags";
+static const char* const KEEP_LABELS = "KeepLabels";
 static const char* const KEEP_SOURCE = "KeepSource";
 static const char* const LEVEL = "Level";
 static const char* const PARENT = "Parent";
@@ -122,6 +123,8 @@
                        "configuration option `DeidentifyLogsDicomVersion` for possible values.", false)
       .SetRequestField(KEEP_PRIVATE_TAGS, RestApiCallDocumentation::Type_Boolean,
                        "Keep the private tags from the DICOM instances (defaults to `false`)", false)
+      .SetRequestField(KEEP_LABELS, RestApiCallDocumentation::Type_Boolean,
+                       "Keep the labels of all resources level (defaults to `false`)", false)
       .SetRequestField(REPLACE, RestApiCallDocumentation::Type_JsonObject,
                        "Associative array to change the value of some DICOM tags in the DICOM instances. " INFO_SUBSEQUENCES, false)
       .SetRequestField(REMOVE, RestApiCallDocumentation::Type_JsonListOfStrings,
@@ -1068,6 +1071,8 @@
                          "Associative array to change the value of some DICOM tags in the new study. "
                          "These tags must be part of the \"Patient Module Attributes\" or the \"General Study "
                          "Module Attributes\", as specified by the DICOM 2011 standard in Tables C.7-1 and C.7-3.", false)
+        .SetRequestField(KEEP_LABELS, RestApiCallDocumentation::Type_Boolean,
+                         "Keep the labels of all resources level (defaults to `false`)", false)
         .SetRequestField(REMOVE, RestApiCallDocumentation::Type_JsonListOfStrings,
                          "List of tags that must be removed in the new study (from the same modules as in the `Replace` option)", false)
         .SetRequestField(KEEP_SOURCE, RestApiCallDocumentation::Type_Boolean,
--- a/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp	Wed Jan 10 07:21:50 2024 +0100
+++ b/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp	Mon Jan 22 17:14:11 2024 +0100
@@ -210,7 +210,11 @@
     
     std::unique_ptr<DicomInstanceHasher> originalHasher;
     std::unique_ptr<ParsedDicomFile> modified;
-
+    std::set<std::string> instanceLabels;
+    std::set<std::string> seriesLabels;
+    std::set<std::string> studyLabels;
+    std::set<std::string> patientLabels;
+ 
     try
     {
       ServerContext::DicomCacheLocker locker(GetContext(), instance);
@@ -234,6 +238,15 @@
       boost::recursive_mutex::scoped_lock lock(mutex_);  // DicomModification object is not thread safe, we must protect it from here
 
       modification_->Apply(*modified);
+
+      if (modification_->AreLabelsKept())
+      {
+        GetContext().GetIndex().ListLabels(instanceLabels, instance, ResourceType_Instance);
+        // we must also save the parent labels.  This instance might currently be the only one in the hierarchy and therefore it might be in charge of restoring all labels of the hierarchy
+        GetContext().GetIndex().ListLabels(seriesLabels, originalHasher->HashSeries(), ResourceType_Series);
+        GetContext().GetIndex().ListLabels(studyLabels, originalHasher->HashStudy(), ResourceType_Study);
+        GetContext().GetIndex().ListLabels(patientLabels, originalHasher->HashPatient(), ResourceType_Patient);
+      }
     }
 
     const std::string modifiedUid = IDicomTranscoder::GetSopInstanceUid(modified->GetDcmtkObject());
@@ -318,6 +331,19 @@
                              "Error while storing a modified instance " + instance);
     }
 
+    {
+      boost::recursive_mutex::scoped_lock lock(mutex_);  // DicomModification object is not thread safe, we must protect it from here
+
+      if (modification_->AreLabelsKept())
+      {
+        GetContext().GetIndex().AddLabels(instance, ResourceType_Instance, instanceLabels);
+        GetContext().GetIndex().AddLabels(modifiedHasher.HashSeries(), ResourceType_Series, seriesLabels);
+        GetContext().GetIndex().AddLabels(modifiedHasher.HashStudy(), ResourceType_Study, studyLabels);
+        GetContext().GetIndex().AddLabels(modifiedHasher.HashPatient(), ResourceType_Patient, patientLabels);
+      }
+    }
+
+
     /**
      * The assertion below will fail if automated transcoding to a
      * lossy transfer syntax is enabled in the Orthanc core, and if