changeset 5666:aa231c18b9d2 find-refactoring

adding computed tags
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 04 Jul 2024 18:31:54 +0200
parents d8c86698110c
children 93dff1fccf36
files OrthancServer/Sources/Database/Compatibility/GenericFind.cpp OrthancServer/Sources/Database/FindRequest.h OrthancServer/Sources/Database/FindResponse.cpp OrthancServer/Sources/Database/FindResponse.h OrthancServer/Sources/ResourceFinder.cpp OrthancServer/Sources/ResourceFinder.h
diffstat 6 files changed, 197 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Thu Jul 04 18:31:54 2024 +0200
@@ -411,16 +411,20 @@
         }
       }
 
-      if (request.GetLevel() != ResourceType_Instance &&
-          request.GetChildrenRetrieveSpecification(GetChildResourceType(request.GetLevel())).IsRetrieveIdentifiers())
+      if (request.GetLevel() != ResourceType_Instance)
       {
         // TODO-FIND: Retrieve other levels than immediate children
-        std::list<std::string> children;
-        transaction_.GetChildrenPublicId(children, internalId);
+        const ResourceType childLevel = GetChildResourceType(request.GetLevel());
 
-        for (std::list<std::string>::const_iterator it = children.begin(); it != children.end(); ++it)
+        if (request.GetChildrenRetrieveSpecification(childLevel).IsRetrieveIdentifiers())
         {
-          resource->AddChildIdentifier(*it);
+          std::list<std::string> children;
+          transaction_.GetChildrenPublicId(children, internalId);
+
+          for (std::list<std::string>::const_iterator it = children.begin(); it != children.end(); ++it)
+          {
+            resource->AddChildIdentifier(childLevel, *it);
+          }
         }
       }
 
@@ -432,7 +436,8 @@
         resource->AddChildrenMetadata(*it, values);
       }
 
-      if (request.IsRetrieveOneInstanceIdentifier())
+      if (request.IsRetrieveOneInstanceIdentifier() &&
+          !request.GetChildrenRetrieveSpecification(ResourceType_Instance).IsRetrieveIdentifiers())
       {
         int64_t currentId = internalId;
         ResourceType currentLevel = level;
@@ -452,7 +457,7 @@
           }
         }
 
-        resource->SetOneInstanceIdentifier(transaction_.GetPublicId(currentId));
+        resource->AddChildIdentifier(ResourceType_Instance, transaction_.GetPublicId(currentId));
       }
 
       response.Add(resource.release());
--- a/OrthancServer/Sources/Database/FindRequest.h	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/Database/FindRequest.h	Thu Jul 04 18:31:54 2024 +0200
@@ -407,7 +407,8 @@
 
     bool IsRetrieveOneInstanceIdentifier() const
     {
-      return retrieveOneInstanceIdentifier_;
+      return (retrieveOneInstanceIdentifier_ ||
+              GetChildrenRetrieveSpecification(ResourceType_Instance).IsRetrieveIdentifiers());
     }
   };
 }
--- a/OrthancServer/Sources/Database/FindResponse.cpp	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.cpp	Thu Jul 04 18:31:54 2024 +0200
@@ -142,11 +142,57 @@
   }
 
 
-  void FindResponse::Resource::AddChildIdentifier(const std::string& identifier)
+  std::set<std::string>& FindResponse::Resource::GetChildrenIdentifiers(ResourceType level)
   {
-    if (childrenIdentifiers_.find(identifier) == childrenIdentifiers_.end())
+    switch (level)
     {
-      childrenIdentifiers_.insert(identifier);
+      case ResourceType_Study:
+        if (level_ == ResourceType_Patient)
+        {
+          return childrenStudiesIdentifiers_;
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+
+      case ResourceType_Series:
+        if (level_ == ResourceType_Patient ||
+            level_ == ResourceType_Study)
+        {
+          return childrenSeriesIdentifiers_;
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+
+      case ResourceType_Instance:
+        if (level_ == ResourceType_Patient ||
+            level_ == ResourceType_Study ||
+            level_ == ResourceType_Series)
+        {
+          return childrenInstancesIdentifiers_;
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  void FindResponse::Resource::AddChildIdentifier(ResourceType level,
+                                                  const std::string& identifier)
+  {
+    std::set<std::string>& target = GetChildrenIdentifiers(level);
+
+    if (target.find(identifier) == target.end())
+    {
+      target.insert(identifier);
     }
     else
     {
@@ -375,49 +421,17 @@
   }
 
 
-  void FindResponse::Resource::SetOneInstanceIdentifier(const std::string& id)
+  const std::string& FindResponse::Resource::GetOneInstanceIdentifier() const
   {
-    if (level_ == ResourceType_Instance)
+    const std::set<std::string>& instances = GetChildrenIdentifiers(ResourceType_Instance);
+
+    if (instances.size() == 0)
     {
-      throw OrthancException(ErrorCode_BadParameterType);
-    }
-    else if (HasOneInstanceIdentifier())
-    {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);  // HasOneInstanceIdentifier() should have been called
     }
     else
     {
-      oneInstanceIdentifier_.reset(new std::string(id));
-    }
-  }
-
-
-  const std::string& FindResponse::Resource::GetOneInstanceIdentifier() const
-  {
-    if (level_ == ResourceType_Instance)
-    {
-      throw OrthancException(ErrorCode_BadParameterType);
-    }
-    else if (HasOneInstanceIdentifier())
-    {
-      return *oneInstanceIdentifier_;
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);
-    }
-  }
-
-
-  bool FindResponse::Resource::HasOneInstanceIdentifier() const
-  {
-    if (level_ == ResourceType_Instance)
-    {
-      throw OrthancException(ErrorCode_BadParameterType);
-    }
-    else
-    {
-      return oneInstanceIdentifier_.get() != NULL;
+      return *instances.begin();
     }
   }
 
@@ -523,9 +537,10 @@
           }
           else
           {
+            const std::set<std::string>& ids = GetChildrenIdentifiers(levels[i]);
+
             Json::Value v = Json::arrayValue;
-            for (std::set<std::string>::const_iterator it = childrenIdentifiers_.begin();
-                 it != childrenIdentifiers_.end(); ++it)
+            for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it)
             {
               v.append(*it);
             }
--- a/OrthancServer/Sources/Database/FindResponse.h	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.h	Thu Jul 04 18:31:54 2024 +0200
@@ -85,11 +85,12 @@
       std::map<MetadataType, std::string>   metadataStudy_;
       std::map<MetadataType, std::string>   metadataSeries_;
       std::map<MetadataType, std::string>   metadataInstance_;
-      std::set<std::string>                 childrenIdentifiers_;
-      std::set<std::string>                 labels_;      
+      std::set<std::string>                 childrenStudiesIdentifiers_;
+      std::set<std::string>                 childrenSeriesIdentifiers_;
+      std::set<std::string>                 childrenInstancesIdentifiers_;
+      std::set<std::string>                 labels_;
       std::map<FileContentType, FileInfo>   attachments_;
       ChildrenMetadata                      childrenMetadata_;
-      std::unique_ptr<std::string>          oneInstanceIdentifier_;
 
       MainDicomTagsAtLevel& GetMainDicomTagsAtLevel(ResourceType level);
 
@@ -98,6 +99,8 @@
         return const_cast<Resource&>(*this).GetMainDicomTagsAtLevel(level);
       }
 
+      std::set<std::string>& GetChildrenIdentifiers(ResourceType level);
+
     public:
       Resource(ResourceType level,
                int64_t internalId,
@@ -167,11 +170,12 @@
                           ResourceType level,
                           MetadataType metadata) const;
 
-      void AddChildIdentifier(const std::string& childId);
+      void AddChildIdentifier(ResourceType level,
+                              const std::string& childId);
 
-      const std::set<std::string>& GetChildrenIdentifiers() const
+      const std::set<std::string>& GetChildrenIdentifiers(ResourceType level) const
       {
-        return childrenIdentifiers_;
+        return const_cast<Resource&>(*this).GetChildrenIdentifiers(level);
       }
 
       void AddLabel(const std::string& label);
@@ -204,9 +208,10 @@
 
       const std::string& GetOneInstanceIdentifier() const;
 
-      void SetOneInstanceIdentifier(const std::string& id);
-
-      bool HasOneInstanceIdentifier() const;
+      bool HasOneInstanceIdentifier() const
+      {
+        return !GetChildrenIdentifiers(ResourceType_Instance).empty();
+      }
 
       void DebugExport(Json::Value& target,
                        const FindRequest& request) const;
--- a/OrthancServer/Sources/ResourceFinder.cpp	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Thu Jul 04 18:31:54 2024 +0200
@@ -35,6 +35,65 @@
 
 namespace Orthanc
 {
+  void ResourceFinder::ConfigureChildrenCountComputedTag(DicomTag tag,
+                                                         ResourceType parentLevel,
+                                                         ResourceType childLevel)
+  {
+    if (request_.GetLevel() == parentLevel)
+    {
+      requestedComputedTags_.insert(tag);
+      hasRequestedTags_ = true;
+      request_.GetChildrenRetrieveSpecification(childLevel).SetRetrieveCount(true);
+    }
+  }
+
+
+  void ResourceFinder::InjectChildrenCountComputedTag(DicomMap& requestedTags,
+                                                      DicomTag tag,
+                                                      const FindResponse::Resource& resource,
+                                                      ResourceType level) const
+  {
+    if (IsRequestedComputedTag(tag))
+    {
+      const std::set<std::string>& children = resource.GetChildrenIdentifiers(level);
+      requestedTags.SetValue(tag, boost::lexical_cast<std::string>(children.size()), false);
+    }
+  }
+
+
+  void ResourceFinder::InjectComputedTags(DicomMap& requestedTags,
+                                          const FindResponse::Resource& resource) const
+  {
+    switch (resource.GetLevel())
+    {
+      case ResourceType_Patient:
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, resource, ResourceType_Study);
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, resource, ResourceType_Series);
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, resource, ResourceType_Instance);
+        break;
+
+      case ResourceType_Study:
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, resource, ResourceType_Series);
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, resource, ResourceType_Instance);
+        break;
+
+      case ResourceType_Series:
+        InjectChildrenCountComputedTag(requestedTags, DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, resource, ResourceType_Instance);
+        break;
+
+      case ResourceType_Instance:
+        if (IsRequestedComputedTag(DICOM_TAG_INSTANCE_AVAILABILITY))
+        {
+          requestedTags.SetValue(DICOM_TAG_INSTANCE_AVAILABILITY, "ONLINE", false);
+        }
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
   SeriesStatus ResourceFinder::GetSeriesStatus(uint32_t& expectedNumberOfInstances,
                                                const FindResponse::Resource& resource) const
   {
@@ -95,34 +154,6 @@
   }
 
 
-  static void InjectRequestedTags(DicomMap& requestedTags,
-                                  std::set<DicomTag>& missingTags /* out */,
-                                  const FindResponse::Resource& resource,
-                                  ResourceType level,
-                                  const std::set<DicomTag>& tags)
-  {
-    if (!tags.empty())
-    {
-      DicomMap m;
-      resource.GetMainDicomTags(m, level);
-
-      for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
-      {
-        std::string value;
-        if (m.LookupStringValue(value, *it, false /* not binary */))
-        {
-          requestedTags.SetValue(*it, value, false /* not binary */);
-        }
-        else
-        {
-          // This is the case where the Housekeeper should be run
-          missingTags.insert(*it);
-        }
-      }
-    }
-  }
-
-
   void ResourceFinder::Expand(Json::Value& target,
                               const FindResponse::Resource& resource,
                               ServerIndex& index) const
@@ -165,7 +196,7 @@
 
     if (resource.GetLevel() != ResourceType_Instance)
     {
-      const std::set<std::string>& children = resource.GetChildrenIdentifiers();
+      const std::set<std::string>& children = resource.GetChildrenIdentifiers(GetChildResourceType(resource.GetLevel()));
 
       Json::Value c = Json::arrayValue;
       for (std::set<std::string>::const_iterator
@@ -482,20 +513,35 @@
 
       hasRequestedTags_ = true;
     }
+    else if (tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Patient, ResourceType_Study);
+    }
+    else if (tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Patient, ResourceType_Series);
+    }
+    else if (tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Patient, ResourceType_Instance);
+    }
+    else if (tag == DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Study, ResourceType_Series);
+    }
+    else if (tag == DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Study, ResourceType_Instance);
+    }
+    else if (tag == DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES)
+    {
+      ConfigureChildrenCountComputedTag(tag, ResourceType_Series, ResourceType_Instance);
+    }
     else if (tag == DICOM_TAG_INSTANCE_AVAILABILITY)
     {
       requestedComputedTags_.insert(tag);
       hasRequestedTags_ = true;
     }
-    else if (tag == DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES)
-    {
-      if (request_.GetLevel() == ResourceType_Series)
-      {
-        requestedComputedTags_.insert(tag);
-        hasRequestedTags_ = true;
-        request_.GetChildrenRetrieveSpecification(ResourceType_Instance).SetRetrieveCount(true);
-      }
-    }
     else
     {
       // This is neither a main DICOM tag, nor a computed DICOM tag:
@@ -521,34 +567,30 @@
   }
 
 
-  void ResourceFinder::InjectComputedTags(DicomMap& requestedTags,
-                                          const FindResponse::Resource& resource) const
+  static void InjectRequestedTags(DicomMap& requestedTags,
+                                  std::set<DicomTag>& missingTags /* out */,
+                                  const FindResponse::Resource& resource,
+                                  ResourceType level,
+                                  const std::set<DicomTag>& tags)
   {
-    switch (resource.GetLevel())
+    if (!tags.empty())
     {
-      case ResourceType_Patient:
-        break;
+      DicomMap m;
+      resource.GetMainDicomTags(m, level);
 
-      case ResourceType_Study:
-        break;
-
-      case ResourceType_Series:
-        if (IsRequestedComputedTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES))
+      for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
+      {
+        std::string value;
+        if (m.LookupStringValue(value, *it, false /* not binary */))
         {
-          requestedTags.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES,
-                                 boost::lexical_cast<std::string>(resource.GetChildrenIdentifiers().size()), false);
+          requestedTags.SetValue(*it, value, false /* not binary */);
         }
-        break;
-
-      case ResourceType_Instance:
-        if (IsRequestedComputedTag(DICOM_TAG_INSTANCE_AVAILABILITY))
+        else
         {
-          requestedTags.SetValue(DICOM_TAG_INSTANCE_AVAILABILITY, "ONLINE", false);
+          // This is the case where the Housekeeper should be run
+          missingTags.insert(*it);
         }
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
+      }
     }
   }
 
--- a/OrthancServer/Sources/ResourceFinder.h	Thu Jul 04 14:36:24 2024 +0200
+++ b/OrthancServer/Sources/ResourceFinder.h	Thu Jul 04 18:31:54 2024 +0200
@@ -52,6 +52,15 @@
       return requestedComputedTags_.find(tag) != requestedComputedTags_.end();
     }
 
+    void ConfigureChildrenCountComputedTag(DicomTag tag,
+                                           ResourceType parentLevel,
+                                           ResourceType childLevel);
+
+    void InjectChildrenCountComputedTag(DicomMap& requestedTags,
+                                        DicomTag tag,
+                                        const FindResponse::Resource& resource,
+                                        ResourceType level) const;
+
     SeriesStatus GetSeriesStatus(uint32_t& expectedNumberOfInstances,
                                  const FindResponse::Resource& resource) const;