changeset 5600:8796c100aaf8 find-refactoring

introduction of ResourceFinder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 May 2024 21:23:34 +0200
parents 81a29ad7fb4b
children aafe402ec950
files OrthancServer/Sources/Database/FindResponse.cpp OrthancServer/Sources/Database/FindResponse.h OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp
diffstat 3 files changed, 371 insertions(+), 271 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Sources/Database/FindResponse.cpp	Tue May 07 18:44:53 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.cpp	Tue May 07 21:23:34 2024 +0200
@@ -404,219 +404,6 @@
   }
 
 
-  SeriesStatus FindResponse::Resource::GetSeriesStatus(uint32_t& expectedNumberOfInstances) const
-  {
-    if (level_ != ResourceType_Series)
-    {
-      throw OrthancException(ErrorCode_BadParameterType);
-    }
-
-    std::string s;
-    if (!LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
-        !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
-    {
-      return SeriesStatus_Unknown;
-    }
-
-    std::list<std::string> values;
-    if (!LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
-    {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);
-    }
-
-    std::set<int64_t> instances;
-
-    for (std::list<std::string>::const_iterator
-           it = values.begin(); it != values.end(); ++it)
-    {
-      int64_t index;
-
-      if (!SerializationToolbox::ParseInteger64(index, *it))
-      {
-        return SeriesStatus_Unknown;
-      }
-
-      if (index <= 0 ||
-          index > static_cast<int64_t>(expectedNumberOfInstances))
-      {
-        // Out-of-range instance index
-        return SeriesStatus_Inconsistent;
-      }
-
-      if (instances.find(index) != instances.end())
-      {
-        // Twice the same instance index
-        return SeriesStatus_Inconsistent;
-      }
-
-      instances.insert(index);
-    }
-
-    if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
-    {
-      return SeriesStatus_Complete;
-    }
-    else
-    {
-      return SeriesStatus_Missing;
-    }
-  }
-
-
-  void FindResponse::Resource::Expand(Json::Value& target,
-                                      const FindRequest& request,
-                                      bool includeAllMetadata) const
-  {
-    /**
-
-       TODO-FIND:
-
-       - Metadata / Series / ExpectedNumberOfInstances
-
-       - Metadata / Series / Status
-
-       - Metadata / Instance / FileSize
-
-       - Metadata / Instance / FileUuid
-
-       - Metadata / Instance / IndexInSeries
-
-       - Metadata / AnonymizedFrom
-
-       - Metadata / ModifiedFrom
-
-     **/
-
-    /**
-     * This method closely follows "SerializeExpandedResource()" in
-     * "ServerContext.cpp" from Orthanc 1.12.3.
-     **/
-
-    target = Json::objectValue;
-    target["ID"] = identifier_;
-    target["Type"] = GetResourceTypeText(level_, false, true);
-
-    if (request.IsRetrieveParentIdentifier())
-    {
-      switch (level_)
-      {
-        case ResourceType_Patient:
-          break;
-
-        case ResourceType_Study:
-          target["ParentPatient"] = GetParentIdentifier();
-          break;
-
-        case ResourceType_Series:
-          target["ParentStudy"] = GetParentIdentifier();
-          break;
-
-        case ResourceType_Instance:
-          target["ParentSeries"] = GetParentIdentifier();
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-
-    if (request.IsRetrieveChildrenIdentifiers())
-    {
-      const std::set<std::string>& children = GetChildrenIdentifiers();
-
-      Json::Value c = Json::arrayValue;
-      for (std::set<std::string>::const_iterator
-             it = children.begin(); it != children.end(); ++it)
-      {
-        c.append(*it);
-      }
-
-      switch (level_)
-      {
-        case ResourceType_Patient:
-          target["Studies"] = c;
-          break;
-
-        case ResourceType_Study:
-          target["Series"] = c;
-          break;
-
-        case ResourceType_Series:
-          target["Instances"] = c;
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-
-    if (request.IsRetrieveMetadata(level_))
-    {
-      switch (level_)
-      {
-        case ResourceType_Patient:
-        case ResourceType_Study:
-          break;
-
-        case ResourceType_Series:
-        {
-          uint32_t expectedNumberOfInstances;
-          SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances);
-
-          target["Status"] = EnumerationToString(status);
-
-          if (status == SeriesStatus_Unknown)
-          {
-            target["ExpectedNumberOfInstances"] = Json::nullValue;
-          }
-          else
-          {
-            target["ExpectedNumberOfInstances"] = Json::nullValue;
-          }
-
-          break;
-        }
-
-        case ResourceType_Instance:
-        {
-          // TODO-FIND
-          break;
-        }
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-
-    if (request.IsRetrieveLabels())
-    {
-      Json::Value labels = Json::arrayValue;
-
-      for (std::set<std::string>::const_iterator it = labels_.begin(); it != labels_.end(); ++it)
-      {
-        labels.append(*it);
-      }
-
-      target["Labels"] = labels;
-    }
-
-    if (request.IsRetrieveMetadata(level_) &&
-        includeAllMetadata)
-    {
-      const std::map<MetadataType, std::string>& m = GetMetadata(level_);
-
-      Json::Value metadata = Json::objectValue;
-
-      for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
-      {
-        metadata[EnumerationToString(it->first)] = it->second;
-      }
-
-      target["Metadata"] = metadata;
-    }
-  }
-
-
   static void DebugDicomMap(Json::Value& target,
                             const DicomMap& m)
   {
--- a/OrthancServer/Sources/Database/FindResponse.h	Tue May 07 18:44:53 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.h	Tue May 07 21:23:34 2024 +0200
@@ -198,12 +198,6 @@
       bool LookupAttachmentOfOneInstance(FileInfo& target,
                                          FileContentType type) const;
 
-      SeriesStatus GetSeriesStatus(uint32_t& expecterNumberOfInstances) const;
-
-      void Expand(Json::Value& target,
-                  const FindRequest& request,
-                  bool includeAllMetadata) const;
-
       void DebugExport(Json::Value& target,
                        const FindRequest& request) const;
     };
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue May 07 18:44:53 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue May 07 21:23:34 2024 +0200
@@ -198,6 +198,371 @@
   }
 
 
+  class ResourceFinder : public boost::noncopyable
+  {
+  private:
+    FindRequest        request_;
+    bool               expand_;
+    std::set<DicomTag> requestedTags_;
+    DicomToJsonFormat  format_;
+    bool               includeAllMetadata_;   // Same as: ExpandResourceFlags_IncludeAllMetadata
+
+    SeriesStatus GetSeriesStatus(uint32_t& expectedNumberOfInstances,
+                                 const FindResponse::Resource& resource) const
+    {
+      if (request_.GetLevel() != ResourceType_Series)
+      {
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+
+      std::string s;
+      if (!resource.LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
+          !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
+      {
+        return SeriesStatus_Unknown;
+      }
+
+      std::list<std::string> values;
+      if (!resource.LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+
+      std::set<int64_t> instances;
+
+      for (std::list<std::string>::const_iterator
+             it = values.begin(); it != values.end(); ++it)
+      {
+        int64_t index;
+
+        if (!SerializationToolbox::ParseInteger64(index, *it))
+        {
+          return SeriesStatus_Unknown;
+        }
+
+        if (index <= 0 ||
+            index > static_cast<int64_t>(expectedNumberOfInstances))
+        {
+          // Out-of-range instance index
+          return SeriesStatus_Inconsistent;
+        }
+
+        if (instances.find(index) != instances.end())
+        {
+          // Twice the same instance index
+          return SeriesStatus_Inconsistent;
+        }
+
+        instances.insert(index);
+      }
+
+      if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
+      {
+        return SeriesStatus_Complete;
+      }
+      else
+      {
+        return SeriesStatus_Missing;
+      }
+    }
+
+
+    void Expand(Json::Value& target,
+                const FindResponse::Resource& resource) const
+    {
+      /**
+
+         TODO-FIND:
+
+         - Metadata / Series / ExpectedNumberOfInstances
+
+         - Metadata / Series / Status
+
+         - Metadata / Instance / FileSize
+
+         - Metadata / Instance / FileUuid
+
+         - Metadata / Instance / IndexInSeries
+
+         - Metadata / AnonymizedFrom
+
+         - Metadata / ModifiedFrom
+
+      **/
+
+      /**
+       * This method closely follows "SerializeExpandedResource()" in
+       * "ServerContext.cpp" from Orthanc 1.12.3.
+       **/
+
+      if (resource.GetLevel() != request_.GetLevel())
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      target = Json::objectValue;
+
+      target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
+      target["ID"] = resource.GetIdentifier();
+
+      switch (resource.GetLevel())
+      {
+        case ResourceType_Patient:
+          break;
+
+        case ResourceType_Study:
+          target["ParentPatient"] = resource.GetParentIdentifier();
+          break;
+
+        case ResourceType_Series:
+          target["ParentStudy"] = resource.GetParentIdentifier();
+          break;
+
+        case ResourceType_Instance:
+          target["ParentSeries"] = resource.GetParentIdentifier();
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (resource.GetLevel() != ResourceType_Instance)
+      {
+        const std::set<std::string>& children = resource.GetChildrenIdentifiers();
+
+        Json::Value c = Json::arrayValue;
+        for (std::set<std::string>::const_iterator
+               it = children.begin(); it != children.end(); ++it)
+        {
+          c.append(*it);
+        }
+
+        switch (resource.GetLevel())
+        {
+          case ResourceType_Patient:
+            target["Studies"] = c;
+            break;
+
+          case ResourceType_Study:
+            target["Series"] = c;
+            break;
+
+          case ResourceType_Series:
+            target["Instances"] = c;
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_InternalError);
+        }
+      }
+
+      switch (resource.GetLevel())
+      {
+        case ResourceType_Patient:
+        case ResourceType_Study:
+          break;
+
+        case ResourceType_Series:
+        {
+          uint32_t expectedNumberOfInstances;
+          SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
+
+          target["Status"] = EnumerationToString(status);
+
+          if (status == SeriesStatus_Unknown)
+          {
+            target["ExpectedNumberOfInstances"] = Json::nullValue;
+          }
+          else
+          {
+            target["ExpectedNumberOfInstances"] = expectedNumberOfInstances;
+          }
+
+          break;
+        }
+
+        case ResourceType_Instance:
+        {
+          FileInfo info;
+          if (resource.LookupAttachment(info, FileContentType_Dicom))
+          {
+            target["FileSize"] = info.GetUncompressedSize();
+            target["FileUuid"] = info.GetUuid();
+          }
+          else
+          {
+            throw OrthancException(ErrorCode_InternalError);
+          }
+
+          std::string s;
+          uint32_t index;
+          if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
+              SerializationToolbox::ParseUnsignedInteger32(index, s))
+          {
+            target["IndexInSeries"] = index;
+          }
+          else
+          {
+            target["IndexInSeries"] = Json::nullValue;
+          }
+
+          break;
+        }
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+
+      std::string s;
+      if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
+      {
+        target["AnonymizedFrom"] = s;
+      }
+
+      if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
+      {
+        target["ModifiedFrom"] = s;
+      }
+
+      if (resource.GetLevel() == ResourceType_Patient ||
+          resource.GetLevel() == ResourceType_Study ||
+          resource.GetLevel() == ResourceType_Series)
+      {
+        // TODO-FIND: Stable
+
+        /*
+          if (resource.IsStable())
+        {
+          target["IsStable"] = true;
+        }
+        */
+
+        if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
+        {
+          target["LastUpdate"] = s;
+        }
+      }
+
+      {
+        Json::Value labels = Json::arrayValue;
+
+        for (std::set<std::string>::const_iterator
+               it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
+        {
+          labels.append(*it);
+        }
+
+        target["Labels"] = labels;
+      }
+
+      if (includeAllMetadata_)
+      {
+        const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
+
+        Json::Value metadata = Json::objectValue;
+
+        for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
+        {
+          metadata[EnumerationToString(it->first)] = it->second;
+        }
+
+        target["Metadata"] = metadata;
+      }
+    }
+
+
+  public:
+    ResourceFinder(ResourceType level,
+                   bool expand) :
+      request_(level),
+      expand_(expand),
+      format_(DicomToJsonFormat_Human),
+      includeAllMetadata_(false)
+    {
+      if (expand)
+      {
+        request_.SetRetrieveMainDicomTags(level, true);
+        request_.SetRetrieveMetadata(level, true);
+        request_.SetRetrieveLabels(true);
+
+        if (level == ResourceType_Series)
+        {
+          request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
+        }
+
+        if (level == ResourceType_Instance)
+        {
+          request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
+        }
+        else
+        {
+          request_.SetRetrieveChildrenIdentifiers(true);
+        }
+
+        if (level != ResourceType_Patient)
+        {
+          request_.SetRetrieveParentIdentifier(true);
+        }
+      }
+    }
+
+    void SetRequestedTags(const std::set<DicomTag>& tags)
+    {
+      requestedTags_ = tags;
+    }
+
+    void SetFormat(DicomToJsonFormat format)
+    {
+      format_ = format;
+    }
+
+    void SetLimits(uint64_t since,
+                   uint64_t count)
+    {
+      request_.SetLimits(since, count);
+    }
+
+    void SetIncludeAllMetadata(bool include)
+    {
+      includeAllMetadata_ = include;
+    }
+
+    void Execute(Json::Value& target,
+                 ServerContext& context)
+    {
+      FindResponse response;
+      context.GetIndex().ExecuteFind(response, request_);
+
+      target = Json::arrayValue;
+
+      if (expand_)
+      {
+        for (size_t i = 0; i < response.GetSize(); i++)
+        {
+          Json::Value item;
+          Expand(item, response.GetResource(i));
+
+#if 0
+          target.append(item);
+#else
+          context.AppendFindResponse(target, request_, response.GetResource(i), format_,
+                                     requestedTags_, true /* allowStorageAccess */);
+          std::cout << "+++ Expected: " << target[target.size() - 1].toStyledString();
+          std::cout << "--- Actual: " << item.toStyledString();
+#endif
+        }
+      }
+      else
+      {
+        for (size_t i = 0; i < response.GetSize(); i++)
+        {
+          target.append(response.GetResource(i).GetIdentifier());
+        }
+      }
+    }
+  };
+
+
   template <enum ResourceType resourceType>
   static void ListResources(RestApiGetCall& call)
   {
@@ -238,36 +603,9 @@
       std::set<DicomTag> requestedTags;
       OrthancRestApi::GetRequestedTags(requestedTags, call);
 
-      const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
-
-      FindRequest request(resourceType);
-
-      if (expand)
-      {
-        // compatibility with default expand option
-        request.SetRetrieveMainDicomTags(resourceType, true);
-        request.SetRetrieveMetadata(resourceType, true);
-        request.SetRetrieveLabels(true);
-
-        if (resourceType == ResourceType_Series)
-        {
-          request.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
-        }
-
-        if (resourceType == ResourceType_Instance)
-        {
-          request.SetRetrieveAttachments(true); // for FileSize & FileUuid
-        }
-        else
-        {
-          request.SetRetrieveChildrenIdentifiers(true);
-        }
-
-        if (resourceType != ResourceType_Patient)
-        {
-          request.SetRetrieveParentIdentifier(true);
-        }
-      }
+      ResourceFinder finder(resourceType, expand);
+      finder.SetRequestedTags(requestedTags);
+      finder.SetFormat(OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human));
 
       if (call.HasArgument("limit") ||
           call.HasArgument("since"))
@@ -288,30 +626,11 @@
 
         uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", ""));
         uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", ""));
-        request.SetLimits(since, limit);
+        finder.SetLimits(since, limit);
       }
 
-      FindResponse response;
-      index.ExecuteFind(response, request);
-
-      // TODO-FIND: put this in an AnswerFindResponse method !
-      Json::Value answer = Json::arrayValue;
-
-      if (expand)
-      {
-        for (size_t i = 0; i < response.GetSize(); i++)
-        {
-          context.AppendFindResponse(answer, request, response.GetResource(i), format, requestedTags, true /* allowStorageAccess */);
-        }
-      }
-      else
-      {
-        for (size_t i = 0; i < response.GetSize(); i++)
-        {
-          answer.append(response.GetResource(i).GetIdentifier());
-        }
-      }
-
+      Json::Value answer;
+      finder.Execute(answer, context);
       call.GetOutput().AnswerJson(answer);
     }
     else