diff OrthancServer/Sources/OrthancWebDav.cpp @ 5809:023a99146dd0 attach-custom-data

merged find-refactoring -> attach-custom-data
author Alain Mazy <am@orthanc.team>
date Tue, 24 Sep 2024 12:53:43 +0200
parents d851a54e49b7
children 593e110de3d8
line wrap: on
line diff
--- a/OrthancServer/Sources/OrthancWebDav.cpp	Tue Sep 24 12:11:25 2024 +0200
+++ b/OrthancServer/Sources/OrthancWebDav.cpp	Tue Sep 24 12:53:43 2024 +0200
@@ -28,6 +28,7 @@
 #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
 #include "../../OrthancFramework/Sources/HttpServer/WebDavStorage.h"
 #include "../../OrthancFramework/Sources/Logging.h"
+#include "ResourceFinder.h"
 #include "Search/DatabaseLookup.h"
 #include "ServerContext.h"
 
@@ -50,6 +51,20 @@
   {
     return boost::posix_time::second_clock::universal_time();
   }
+
+
+  static void ParseTime(boost::posix_time::ptime& target,
+                        const std::string& value)
+  {
+    try
+    {
+      target = boost::posix_time::from_iso_string(value);
+    }
+    catch (std::exception& e)
+    {
+      target = GetNow();
+    }
+  }
   
 
   static void LookupTime(boost::posix_time::ptime& target,
@@ -62,17 +77,12 @@
     int64_t revision;  // Ignored
     if (context.GetIndex().LookupMetadata(value, revision, publicId, level, metadata))
     {
-      try
-      {
-        target = boost::posix_time::from_iso_string(value);
-        return;
-      }
-      catch (std::exception& e)
-      {
-      }
+      ParseTime(target, value);
     }
-
-    target = GetNow();
+    else
+    {
+      target = GetNow();
+    }
   }
 
   
@@ -169,6 +179,98 @@
   };
 
   
+  class OrthancWebDav::DicomIdentifiersVisitorV2 : public ResourceFinder::IVisitor
+  {
+  private:
+    bool         isComplete_;
+    Collection&  target_;
+
+  public:
+    explicit DicomIdentifiersVisitorV2(Collection& target) :
+      isComplete_(false),
+      target_(target)
+    {
+    }
+
+    virtual void MarkAsComplete() ORTHANC_OVERRIDE
+    {
+      isComplete_ = true;  // TODO
+    }
+
+    virtual void Apply(const FindResponse::Resource& resource,
+                       const DicomMap& requestedTags)  ORTHANC_OVERRIDE
+    {
+      DicomMap resourceTags;
+      resource.GetMainDicomTags(resourceTags, resource.GetLevel());
+
+      std::string uid;
+      bool hasUid;
+
+      std::string time;
+      bool hasTime;
+
+      switch (resource.GetLevel())
+      {
+        case ResourceType_Study:
+          hasUid = resourceTags.LookupStringValue(uid, DICOM_TAG_STUDY_INSTANCE_UID, false);
+          hasTime = resource.LookupMetadata(time, resource.GetLevel(), MetadataType_LastUpdate);
+          break;
+
+        case ResourceType_Series:
+          hasUid = resourceTags.LookupStringValue(uid, DICOM_TAG_SERIES_INSTANCE_UID, false);
+          hasTime = resource.LookupMetadata(time, resource.GetLevel(), MetadataType_LastUpdate);
+          break;
+
+        case ResourceType_Instance:
+          hasUid = resourceTags.LookupStringValue(uid, DICOM_TAG_SOP_INSTANCE_UID, false);
+          hasTime = resource.LookupMetadata(time, resource.GetLevel(), MetadataType_Instance_ReceptionDate);
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (hasUid &&
+          !uid.empty())
+      {
+        std::unique_ptr<Resource> item;
+
+        if (resource.GetLevel() == ResourceType_Instance)
+        {
+          FileInfo info;
+          if (resource.LookupAttachment(info, FileContentType_Dicom))
+          {
+            std::unique_ptr<File> f(new File(uid + ".dcm"));
+            f->SetMimeType(MimeType_Dicom);
+            f->SetContentLength(info.GetUncompressedSize());
+            item.reset(f.release());
+          }
+        }
+        else
+        {
+          item.reset(new Folder(uid));
+        }
+
+        if (item.get() != NULL)
+        {
+          if (hasTime)
+          {
+            boost::posix_time::ptime t;
+            ParseTime(t, time);
+            item->SetCreationTime(t);
+          }
+          else
+          {
+            item->SetCreationTime(GetNow());
+          }
+
+          target_.AddResource(item.release());
+        }
+      }
+    }
+  };
+
+
   class OrthancWebDav::DicomFileVisitor : public ServerContext::ILookupVisitor
   {
   private:
@@ -221,6 +323,60 @@
   };
   
 
+  class OrthancWebDav::DicomFileVisitorV2 : public ResourceFinder::IVisitor
+  {
+  private:
+    ServerContext&  context_;
+    bool            success_;
+    std::string&    target_;
+    boost::posix_time::ptime&  time_;
+
+  public:
+    DicomFileVisitorV2(ServerContext& context,
+                       std::string& target,
+                       boost::posix_time::ptime& time) :
+      context_(context),
+      success_(false),
+      target_(target),
+      time_(time)
+    {
+    }
+
+    bool IsSuccess() const
+    {
+      return success_;
+    }
+
+    virtual void MarkAsComplete() ORTHANC_OVERRIDE
+    {
+    }
+
+    virtual void Apply(const FindResponse::Resource& resource,
+                       const DicomMap& requestedTags) ORTHANC_OVERRIDE
+    {
+      if (success_)
+      {
+        success_ = false;  // Two matches => Error
+      }
+      else
+      {
+        std::string s;
+        if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_ReceptionDate))
+        {
+          ParseTime(time_, s);
+        }
+        else
+        {
+          time_ = GetNow();
+        }
+
+        context_.ReadDicom(target_, resource.GetIdentifier());
+        success_ = true;
+      }
+    }
+  };
+
+
   class OrthancWebDav::OrthancJsonVisitor : public ServerContext::ILookupVisitor
   {
   private:
@@ -955,7 +1111,7 @@
     std::string  year_;
     std::string  month_;
 
-    class Visitor : public ServerContext::ILookupVisitor
+    class Visitor : public ResourceFinder::IVisitor
     {
     private:
       std::list<std::string>&  resources_;
@@ -966,21 +1122,14 @@
       {
       }
 
-      virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-      {
-        return false;   // (*)
-      }
-      
       virtual void MarkAsComplete() ORTHANC_OVERRIDE
       {
       }
 
-      virtual void Visit(const std::string& publicId,
-                         const std::string& instanceId   /* unused     */,
-                         const DicomMap& mainDicomTags,
-                         const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
+      virtual void Apply(const FindResponse::Resource& resource,
+                         const DicomMap& requestedTags) ORTHANC_OVERRIDE
       {
-        resources_.push_back(publicId);
+        resources_.push_back(resource.GetIdentifier());
       }
     };
     
@@ -992,7 +1141,10 @@
                               true /* case sensitive */, true /* mandatory tag */);
 
       Visitor visitor(resources);
-      GetContext().Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
+
+      ResourceFinder finder(ResourceType_Study, false /* no expand */);
+      finder.SetDatabaseLookup(query);
+      finder.Execute(visitor, GetContext());
     }
 
     virtual INode* CreateResourceNode(const std::string& resource) ORTHANC_OVERRIDE
@@ -1025,7 +1177,7 @@
     std::string       year_;
     const Templates&  templates_;
 
-    class Visitor : public ServerContext::ILookupVisitor
+    class Visitor : public ResourceFinder::IVisitor
     {
     private:
       std::set<std::string> months_;
@@ -1036,20 +1188,16 @@
         return months_;
       }
       
-      virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-      {
-        return false;   // (*)
-      }
-      
       virtual void MarkAsComplete() ORTHANC_OVERRIDE
       {
       }
 
-      virtual void Visit(const std::string& publicId,
-                         const std::string& instanceId   /* unused     */,
-                         const DicomMap& mainDicomTags,
-                         const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
+      virtual void Apply(const FindResponse::Resource& resource,
+                         const DicomMap& requestedTags) ORTHANC_OVERRIDE
       {
+        DicomMap mainDicomTags;
+        resource.GetMainDicomTags(mainDicomTags, ResourceType_Study);
+
         std::string s;
         if (mainDicomTags.LookupStringValue(s, DICOM_TAG_STUDY_DATE, false) &&
             s.size() == 8)
@@ -1071,7 +1219,10 @@
                               true /* case sensitive */, true /* mandatory tag */);
 
       Visitor visitor;
-      context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
+
+      ResourceFinder finder(ResourceType_Study, false /* no expand */);
+      finder.SetDatabaseLookup(query);
+      finder.Execute(visitor, context_);
 
       for (std::set<std::string>::const_iterator it = visitor.GetMonths().begin();
            it != visitor.GetMonths().end(); ++it)
@@ -1165,7 +1316,7 @@
   };
 
 
-  class OrthancWebDav::DicomDeleteVisitor : public ServerContext::ILookupVisitor
+  class OrthancWebDav::DicomDeleteVisitor : public ResourceFinder::IVisitor
   {
   private:
     ServerContext&  context_;
@@ -1179,22 +1330,15 @@
     {
     }
 
-    virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-    {
-      return false;   // (*)
-    }
-      
     virtual void MarkAsComplete() ORTHANC_OVERRIDE
     {
     }
 
-    virtual void Visit(const std::string& publicId,
-                       const std::string& instanceId   /* unused     */,
-                       const DicomMap& mainDicomTags   /* unused     */,
-                       const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
+    virtual void Apply(const FindResponse::Resource& resource,
+                       const DicomMap& requestedTags) ORTHANC_OVERRIDE
     {
       Json::Value info;
-      context_.DeleteResource(info, publicId, level_);
+      context_.DeleteResource(info, resource.GetIdentifier(), level_);
     }
   };
   
@@ -1455,9 +1599,48 @@
         return false;
       }
 
-      DicomIdentifiersVisitor visitor(context_, collection, level);
-      context_.Apply(visitor, query, level, 0 /* since */, limit);
-      
+      if (true)
+      {
+        /**
+         * EXPERIMENTAL VERSION
+         **/
+
+        ResourceFinder finder(level, false /* don't expand */);
+        finder.SetDatabaseLookup(query);
+        finder.SetRetrieveMetadata(true);
+
+        switch (level)
+        {
+          case ResourceType_Study:
+            finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID);
+            break;
+
+          case ResourceType_Series:
+            finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID);
+            break;
+
+          case ResourceType_Instance:
+            finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID);
+            finder.SetRetrieveAttachments(true);
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_InternalError);
+        }
+
+        DicomIdentifiersVisitorV2 visitor(collection);
+        finder.Execute(visitor, context_);
+      }
+      else
+      {
+        /**
+         * VERSION IN ORTHANC <= 1.12.4
+         **/
+
+        DicomIdentifiersVisitor visitor(context_, collection, level);
+        context_.Apply(visitor, query, level, 0 /* since */, limit);
+      }
+
       return true;
     }
     else if (path[0] == BY_PATIENTS ||
@@ -1478,6 +1661,33 @@
   }
 
   
+  static bool GetOrthancJson(std::string& target,
+                             ServerContext& context,
+                             ResourceType level,
+                             const DatabaseLookup& query)
+  {
+    ResourceFinder finder(level, true /* expand */);
+    finder.SetDatabaseLookup(query);
+
+    Json::Value expanded;
+    finder.Execute(expanded, context, DicomToJsonFormat_Human, false /* don't add "Metadata" */);
+
+    if (expanded.size() != 1)
+    {
+      return false;
+    }
+    else
+    {
+      target = expanded[0].toStyledString();
+
+      // Replace UNIX newlines with DOS newlines
+      boost::replace_all(target, "\n", "\r\n");
+
+      return true;
+    }
+  }
+
+
   bool OrthancWebDav::GetFileContent(MimeType& mime,
                                      std::string& content,
                                      boost::posix_time::ptime& modificationTime, 
@@ -1495,12 +1705,25 @@
         DatabaseLookup query;
         query.AddRestConstraint(DICOM_TAG_STUDY_INSTANCE_UID, path[1],
                                 true /* case sensitive */, true /* mandatory tag */);
-      
-        OrthancJsonVisitor visitor(context_, content, ResourceType_Study);
-        context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
 
         mime = MimeType_Json;
-        return visitor.IsSuccess();
+
+        if (true)
+        {
+          /**
+           * EXPERIMENTAL VERSION
+           **/
+          return GetOrthancJson(content, context_, ResourceType_Study, query);
+        }
+        else
+        {
+          /**
+           * VERSION IN ORTHANC <= 1.12.4
+           **/
+          OrthancJsonVisitor visitor(context_, content, ResourceType_Study);
+          context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
+          return visitor.IsSuccess();
+        }
       }
       else if (path.size() == 4 &&
                path[3] == SERIES_INFO)
@@ -1511,11 +1734,24 @@
         query.AddRestConstraint(DICOM_TAG_SERIES_INSTANCE_UID, path[2],
                                 true /* case sensitive */, true /* mandatory tag */);
       
-        OrthancJsonVisitor visitor(context_, content, ResourceType_Series);
-        context_.Apply(visitor, query, ResourceType_Series, 0 /* since */, 0 /* no limit */);
+        mime = MimeType_Json;
 
-        mime = MimeType_Json;
-        return visitor.IsSuccess();
+        if (true)
+        {
+          /**
+           * EXPERIMENTAL VERSION
+           **/
+          return GetOrthancJson(content, context_, ResourceType_Series, query);
+        }
+        else
+        {
+          /**
+           * VERSION IN ORTHANC <= 1.12.4
+           **/
+          OrthancJsonVisitor visitor(context_, content, ResourceType_Series);
+          context_.Apply(visitor, query, ResourceType_Series, 0 /* since */, 0 /* no limit */);
+          return visitor.IsSuccess();
+        }
       }
       else if (path.size() == 4 &&
                boost::ends_with(path[3], ".dcm"))
@@ -1530,11 +1766,32 @@
         query.AddRestConstraint(DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid,
                                 true /* case sensitive */, true /* mandatory tag */);
       
-        DicomFileVisitor visitor(context_, content, modificationTime);
-        context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */);
-        
         mime = MimeType_Dicom;
-        return visitor.IsSuccess();
+
+        if (true)
+        {
+          /**
+           * EXPERIMENTAL VERSION
+           **/
+          ResourceFinder finder(ResourceType_Instance, false /* no expand */);
+          finder.SetDatabaseLookup(query);
+          finder.SetRetrieveMetadata(true);
+          finder.SetRetrieveAttachments(true);
+
+          DicomFileVisitorV2 visitor(context_, content, modificationTime);
+          finder.Execute(visitor, context_);
+
+          return visitor.IsSuccess();
+        }
+        else
+        {
+          /**
+           * VERSION IN ORTHANC <= 1.12.4
+           **/
+          DicomFileVisitor visitor(context_, content, modificationTime);
+          context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */);
+          return visitor.IsSuccess();
+        }
       }
       else
       {
@@ -1655,7 +1912,10 @@
         }
 
         DicomDeleteVisitor visitor(context_, level);
-        context_.Apply(visitor, query, level, 0 /* since */, 0 /* no limit */);
+
+        ResourceFinder finder(level, false /* no expand */);
+        finder.SetDatabaseLookup(query);
+        finder.Execute(visitor, context_);
         return true;
       }
       else