changeset 4934:94a7b681b340 more-tags

added configuration for extra main dicom tags + save signature in metadata + show warning if inconsistent main dicom tags
author Alain Mazy <am@osimis.io>
date Thu, 10 Mar 2022 09:03:24 +0100
parents 312c6f4da888
children acd3f72e2a21
files OrthancServer/Resources/Configuration.json OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/OrthancConfiguration.h OrthancServer/Sources/OrthancInitialization.cpp OrthancServer/Sources/ServerEnumerations.cpp OrthancServer/Sources/ServerEnumerations.h OrthancServer/Sources/ServerToolbox.cpp
diffstat 8 files changed, 131 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Resources/Configuration.json	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Resources/Configuration.json	Thu Mar 10 09:03:24 2022 +0100
@@ -858,5 +858,36 @@
   // (default behaviour).  A value > 1 is meaningful only if the storage
   // is a distributed network storage (e.g object storage plugin).
   // (new experimental feature in Orthanc 1.10.0)
-  "ZipLoaderThreads": 0
+  "ZipLoaderThreads": 0,
+
+  // Extra Main Dicom tags that are stored in DB together with all default
+  // Main Dicom tags that are already stored (TODO: see book new page). 
+  // (new in Orthanc 1.11.0)
+  /**
+  "ExtraMainDicomTags" : {
+    "Instance" : [
+      "Rows",
+      "Columns",
+      "ImageType",
+      "SOPClassUID",
+      "ContentDate",
+      "ContentTime",
+      "FrameOfReferenceUID",
+      "PixelSpacing",
+      "SpecificCharacterSet",
+      "BitsAllocated"
+    ],
+    "Series" : [],
+    "Study": [],
+    "Patient": []
+  },
+  */
+
+  // Enables/disables a warning notifying you when you try to access a
+  // resource that has been saved with a different version of the 
+  // ExtraMainDicomTags list
+  // TODO: see book new page
+  // (new in Orthanc 1.11.0)
+  "EnableLogsForInconsistentMainDicomTags": true
+
 }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu Mar 10 09:03:24 2022 +0100
@@ -941,6 +941,20 @@
               throw OrthancException(ErrorCode_InternalError);
           }
 
+          // check the main dicom tags list has not changed since the resource was stored
+          std::string resourceMainDicomTagsSignature = DicomMap::GetDefaultMainDicomTagsSignature(type);
+          LookupStringMetadata(resourceMainDicomTagsSignature, metadata, MetadataType_MainDicomTagsSignature);
+          
+          if (resourceMainDicomTagsSignature != DicomMap::GetMainDicomTagsSignature(type))
+          {
+            OrthancConfiguration::ReaderLock lock;
+            if (lock.GetConfiguration().IsInconsistentDicomTagsLogsEnabled())
+            {
+              LOG(WARNING) << Orthanc::GetResourceTypeText(type, false , false) << " has been stored with another version of Main Dicom Tags list, you should POST to /" << Orthanc::GetResourceTypeText(type, true, false) << "/" << tuple.get<2>() << "/reconstruct to update the list of tags saved in DB.  Some tags might be missing from this answer.";
+            }
+          }
+
+
           // Record the remaining information
           target["ID"] = tuple.get<2>();
           MainDicomTagsToJson(transaction, target, internalId, type, tuple.get<4>());
@@ -2700,7 +2714,13 @@
           content.AddResource(study, ResourceType_Study, summary_);
           content.AddResource(series, ResourceType_Series, summary_);
           content.AddResource(instance, ResourceType_Instance, summary_);
+
           transaction.SetResourcesContent(content);
+
+          ReplaceMetadata(transaction, patient, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient));    // New in Orthanc 1.11.0
+          ReplaceMetadata(transaction, study, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study));        // New in Orthanc 1.11.0
+          ReplaceMetadata(transaction, series, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series));      // New in Orthanc 1.11.0
+          ReplaceMetadata(transaction, instance, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance));  // New in Orthanc 1.11.0
         }
 
         if (hasTransferSyntax_)
@@ -2715,6 +2735,7 @@
         {
           ReplaceMetadata(transaction, instance, MetadataType_Instance_SopClassUid, value->GetContent());
         }
+
       }
     };
 
@@ -3081,25 +3102,28 @@
             // Populate the tags of the newly-created resources
 
             content.AddResource(instanceId, ResourceType_Instance, dicomSummary_);
+            content.AddMetadata(instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance));  // New in Orthanc 1.11.0
 
             if (status.isNewSeries_)
             {
               content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_);
+              content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series));  // New in Orthanc 1.11.0
             }
 
             if (status.isNewStudy_)
             {
               content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_);
+              content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study));  // New in Orthanc 1.11.0
             }
 
             if (status.isNewPatient_)
             {
               content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_);
+              content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient));  // New in Orthanc 1.11.0
             }
 
 
             // Attach the user-specified metadata
-            // MORE_TAGS: TODO store the mainDicomTags list in metadata
 
             for (MetadataMap::const_iterator 
                    it = metadata_.begin(); it != metadata_.end(); ++it)
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Thu Mar 10 09:03:24 2022 +0100
@@ -1055,6 +1055,10 @@
     }
   }
 
+  bool OrthancConfiguration::IsInconsistentDicomTagsLogsEnabled() const
+  {
+    return GetBooleanParameter("EnableLogsForInconsistentMainDicomTags", true);
+  }
   
   void OrthancConfiguration::DefaultExtractDicomSummary(DicomMap& target,
                                                         const ParsedDicomFile& dicom)
--- a/OrthancServer/Sources/OrthancConfiguration.h	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/OrthancConfiguration.h	Thu Mar 10 09:03:24 2022 +0100
@@ -242,6 +242,8 @@
 
     std::string GetDatabaseServerIdentifier() const;
 
+    bool IsInconsistentDicomTagsLogsEnabled() const;
+
     static void DefaultExtractDicomSummary(DicomMap& target,
                                            const ParsedDicomFile& dicom);
 
--- a/OrthancServer/Sources/OrthancInitialization.cpp	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/OrthancInitialization.cpp	Thu Mar 10 09:03:24 2022 +0100
@@ -200,6 +200,64 @@
     }
   }
 
+  static void LoadMainDicomTags(const Json::Value& configuration)
+  {
+    static const char* const EXTRA_MAIN_DICOM_TAGS = "ExtraMainDicomTags";
+    
+    if (configuration.type() != Json::objectValue ||
+        !configuration.isMember(EXTRA_MAIN_DICOM_TAGS) ||
+        configuration[EXTRA_MAIN_DICOM_TAGS].type() != Json::objectValue)
+    {
+      return;
+    }
+
+    Json::Value::Members levels(configuration[EXTRA_MAIN_DICOM_TAGS].getMemberNames());
+
+    for (Json::Value::ArrayIndex i = 0; i < levels.size(); i++)
+    {
+      ResourceType level;
+      if (levels[i] == "Patient")
+      {
+        level = ResourceType_Patient;
+      }
+      else if (levels[i] == "Study")
+      {
+        level = ResourceType_Study;
+      }
+      else if (levels[i] == "Series")
+      {
+        level = ResourceType_Series;
+      }
+      else if (levels[i] == "Instance")
+      {
+        level = ResourceType_Instance;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat, "Unknown entry '" + levels[i] + "' in ExtraMainDicomTags.");
+      }
+
+      const Json::Value& content = configuration[EXTRA_MAIN_DICOM_TAGS][levels[i]];
+
+      if (content.type() != Json::arrayValue)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat, "The definition of the '" + levels[i] + "' ExtraMainDicomTags entry is invalid (not an array).");
+      }
+
+      if (content.size() > 0)
+      {
+        LOG(INFO) << "Configured Extra Main Dicom Tags for " << levels[i] << ":";
+
+        for (Json::Value::ArrayIndex t = 0; t < content.size(); t++)
+        {
+          const std::string& tagName = content[t].asString();
+          DicomTag tag(FromDcmtkBridge::ParseTag(tagName));
+          DicomMap::AddMainDicomTag(tag, tagName, level);
+          LOG(INFO) << "  - " << tagName;
+        }
+      }
+    }
+  }
 
   static void ConfigurePkcs11(const Json::Value& config)
   {
@@ -299,6 +357,8 @@
     LoadExternalDictionaries(lock.GetJson());  // New in Orthanc 1.9.4
     LoadCustomDictionary(lock.GetJson());
 
+    LoadMainDicomTags(lock.GetJson());  // New in Orthanc 1.11.0
+
     lock.GetConfiguration().RegisterFont(ServerResources::FONT_UBUNTU_MONO_BOLD_16);
 
 #if HAVE_MALLOPT == 1
--- a/OrthancServer/Sources/ServerEnumerations.cpp	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/ServerEnumerations.cpp	Thu Mar 10 09:03:24 2022 +0100
@@ -60,6 +60,7 @@
     dictMetadataType_.Add(MetadataType_Instance_CalledAet, "CalledAET");
     dictMetadataType_.Add(MetadataType_Instance_HttpUsername, "HttpUsername");
     dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset");
+    dictMetadataType_.Add(MetadataType_MainDicomTagsSignature, "MainDicomTagsSignature");
 
     dictContentType_.Add(FileContentType_Dicom, "dicom");
     dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json");
--- a/OrthancServer/Sources/ServerEnumerations.h	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/ServerEnumerations.h	Thu Mar 10 09:03:24 2022 +0100
@@ -151,7 +151,8 @@
     MetadataType_Instance_CalledAet = 12,        // New in Orthanc 1.4.0
     MetadataType_Instance_HttpUsername = 13,     // New in Orthanc 1.4.0
     MetadataType_Instance_PixelDataOffset = 14,  // New in Orthanc 1.9.0
-
+    MetadataType_MainDicomTagsSignature = 15,    // New in Orthanc 1.11.0
+    
     // Make sure that the value "65535" can be stored into this enumeration
     MetadataType_StartUser = 1024,
     MetadataType_EndUser = 65535
--- a/OrthancServer/Sources/ServerToolbox.cpp	Wed Mar 09 12:16:45 2022 +0100
+++ b/OrthancServer/Sources/ServerToolbox.cpp	Thu Mar 10 09:03:24 2022 +0100
@@ -107,29 +107,7 @@
       // example). Take this improvement into consideration for the
       // next upgrade of the database schema.
 
-      const char* plural = NULL;
-
-      switch (level)
-      {
-        case ResourceType_Patient:
-          plural = "patients";
-          break;
-
-        case ResourceType_Study:
-          plural = "studies";
-          break;
-
-        case ResourceType_Series:
-          plural = "series";
-          break;
-
-        case ResourceType_Instance:
-          plural = "instances";
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
+      const char* plural = Orthanc::GetResourceTypeText(level, true, true);
 
       LOG(WARNING) << "Upgrade: Reconstructing the main DICOM tags of all the " << plural << "...";
 
@@ -181,8 +159,11 @@
           transaction.ClearMainDicomTags(resource);
 
           ResourcesContent tags(false /* prevent the setting of metadata */);
-          tags.AddResource(resource, level, dicomSummary);  // MORE_TAGS: re-set the dicomMainTagsList metadata
+          tags.AddResource(resource, level, dicomSummary);
           transaction.SetResourcesContent(tags);
+
+          transaction.DeleteMetadata(resource, MetadataType_MainDicomTagsSignature);
+          transaction.SetMetadata(resource, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(level), 0);
         }
         catch (OrthancException&)
         {